From b4b361542a88da944a1288f47ad234c0747000e8 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Wed, 9 Sep 2026 12:05:46 +0000 Subject: [PATCH 1/5] specs(agents): 59 .t27 specs stating every skill (26) and cron (33) Each published agent skill and each scheduled job gets a .t27 module under specs/skills and specs/crons (vendored copy under public/t27/files, canonical home gHashTag/t27). The compiler wasm typechecks all 59 with nothing discarded; the field schema is checked by the generator that reads them. --- ...ltibots-telegraf-agent-autopilot-L1118.t27 | 27 ++++++++++++ ...tibots-telegraf-bot-owner-billing-L474.t27 | 27 ++++++++++++ ...ltibots-telegraf-check-stuck-trainings.t27 | 26 ++++++++++++ .../specs/crons/999-multibots-telegraf-ci.t27 | 26 ++++++++++++ ...9-multibots-telegraf-crmProactive-L375.t27 | 27 ++++++++++++ ...multibots-telegraf-daily-sales-advisor.t27 | 26 ++++++++++++ ...-multibots-telegraf-generate-jobs-L164.t27 | 26 ++++++++++++ .../999-multibots-telegraf-health-check.t27 | 25 +++++++++++ .../999-multibots-telegraf-log-monitor.t27 | 25 +++++++++++ ...bots-telegraf-notificationHandler-L318.t27 | 27 ++++++++++++ ...bots-telegraf-notificationHandler-L324.t27 | 26 ++++++++++++ ...telegraf-periodic-webhook-health-check.t27 | 25 +++++++++++ ...tibots-telegraf-production-monitor-L80.t27 | 28 +++++++++++++ ...-telegraf-provider-health-monitor-L344.t27 | 27 ++++++++++++ ...ultibots-telegraf-render-server-L10845.t27 | 26 ++++++++++++ ...multibots-telegraf-render-server-L2301.t27 | 26 ++++++++++++ ...multibots-telegraf-render-server-L2325.t27 | 27 ++++++++++++ ...-multibots-telegraf-render-server-L919.t27 | 27 ++++++++++++ .../crons/999-multibots-telegraf-security.t27 | 26 ++++++++++++ .../999-multibots-telegraf-skill-detector.t27 | 25 +++++++++++ .../999-multibots-telegraf-taskCache-L27.t27 | 26 ++++++++++++ ...ltibots-telegraf-video-task-store-L156.t27 | 26 ++++++++++++ .../public/t27/files/specs/crons/README.md | 41 +++++++++++++++++++ .../specs/crons/railway-jobsearch-cron.t27 | 28 +++++++++++++ .../files/specs/crons/t27-formal-mutation.t27 | 26 ++++++++++++ .../files/specs/crons/t27-pr-dashboard.t27 | 25 +++++++++++ .../crons/trinity-agent-cron-cleanup.t27 | 25 +++++++++++ .../specs/crons/trinity-agent-queue-drain.t27 | 25 +++++++++++ .../files/specs/crons/trinity-brain-ci.t27 | 25 +++++++++++ .../t27/files/specs/crons/trinity-codegen.t27 | 25 +++++++++++ .../specs/crons/trinity-discover-callers.t27 | 25 +++++++++++ .../crons/trinity-pages-health-check.t27 | 26 ++++++++++++ .../crons/trinity-signal-health-self.t27 | 25 +++++++++++ .../specs/crons/trinity-site-live-gate.t27 | 25 +++++++++++ .../public/t27/files/specs/skills/README.md | 38 +++++++++++++++++ .../files/specs/skills/t27-measure-corpus.t27 | 26 ++++++++++++ .../t27/files/specs/skills/t27-phi-loop.t27 | 26 ++++++++++++ .../files/specs/skills/t27-tri-pipeline.t27 | 26 ++++++++++++ .../public/t27/files/specs/skills/t27-tri.t27 | 26 ++++++++++++ .../t27/files/specs/skills/t27-wave-audit.t27 | 26 ++++++++++++ .../t27/files/specs/skills/t27-wrap-up.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-blog-post.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-board-sync.t27 | 26 ++++++++++++ .../t27/files/specs/skills/trinity-cloud.t27 | 26 ++++++++++++ .../t27/files/specs/skills/trinity-doctor.t27 | 26 ++++++++++++ .../specs/skills/trinity-farm-garden.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-fpga-synth.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-god-mode.t27 | 26 ++++++++++++ .../specs/skills/trinity-implement-issue.t27 | 26 ++++++++++++ .../skills/trinity-queen-hive-visuals.t27 | 26 ++++++++++++ .../specs/skills/trinity-review-code.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-run-tests.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-scholar.t27 | 26 ++++++++++++ .../t27/files/specs/skills/trinity-status.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-tech-tree.t27 | 26 ++++++++++++ .../t27/files/specs/skills/trinity-tri.t27 | 26 ++++++++++++ .../specs/skills/trinity-trinity-test.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-ux-wave.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-vibee-gen.t27 | 26 ++++++++++++ .../files/specs/skills/trinity-vsa-verify.t27 | 26 ++++++++++++ .../t27/files/specs/skills/trinity-wave.t27 | 26 ++++++++++++ 61 files changed, 1612 insertions(+) create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-check-stuck-trainings.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-ci.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-crmProactive-L375.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-daily-sales-advisor.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-generate-jobs-L164.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-health-check.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-log-monitor.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L318.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L324.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-production-monitor-L80.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L10845.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2301.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2325.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L919.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-security.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-skill-detector.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-taskCache-L27.t27 create mode 100644 apps/website/public/t27/files/specs/crons/999-multibots-telegraf-video-task-store-L156.t27 create mode 100644 apps/website/public/t27/files/specs/crons/README.md create mode 100644 apps/website/public/t27/files/specs/crons/railway-jobsearch-cron.t27 create mode 100644 apps/website/public/t27/files/specs/crons/t27-formal-mutation.t27 create mode 100644 apps/website/public/t27/files/specs/crons/t27-pr-dashboard.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-agent-cron-cleanup.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-agent-queue-drain.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-brain-ci.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-codegen.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-discover-callers.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-pages-health-check.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-signal-health-self.t27 create mode 100644 apps/website/public/t27/files/specs/crons/trinity-site-live-gate.t27 create mode 100644 apps/website/public/t27/files/specs/skills/README.md create mode 100644 apps/website/public/t27/files/specs/skills/t27-measure-corpus.t27 create mode 100644 apps/website/public/t27/files/specs/skills/t27-phi-loop.t27 create mode 100644 apps/website/public/t27/files/specs/skills/t27-tri-pipeline.t27 create mode 100644 apps/website/public/t27/files/specs/skills/t27-tri.t27 create mode 100644 apps/website/public/t27/files/specs/skills/t27-wave-audit.t27 create mode 100644 apps/website/public/t27/files/specs/skills/t27-wrap-up.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-blog-post.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-board-sync.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-cloud.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-doctor.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-farm-garden.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-fpga-synth.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-god-mode.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-implement-issue.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-queen-hive-visuals.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-review-code.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-run-tests.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-scholar.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-status.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-tech-tree.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-tri.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-trinity-test.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-ux-wave.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-vibee-gen.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-vsa-verify.t27 create mode 100644 apps/website/public/t27/files/specs/skills/trinity-wave.t27 diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27 new file mode 100644 index 0000000000..7b7cab9046 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27 @@ -0,0 +1,27 @@ +// 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."; +pub const SUMMARY_RU : str = "Один цикл автопилота — проход генерации и затем тик канала; тик пропускается целиком, если предыдущий цикл ещё идёт."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27 new file mode 100644 index 0000000000..2faaf76a12 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27 @@ -0,0 +1,27 @@ +// 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."; +pub const SUMMARY_RU : str = "Пересчитывает долг каждого владельца бота по журналу платежей, предупреждает на мягком, тревожном и критическом порогах и отключает бота, три дня остающегося критическим."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-check-stuck-trainings.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-check-stuck-trainings.t27 new file mode 100644 index 0000000000..8d4085f603 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-check-stuck-trainings.t27 @@ -0,0 +1,26 @@ +// 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 SUMMARY_RU : str = "Находит обучения моделей, застрявшие в pending или processing дольше порога, и сверяет их с 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-ci.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-ci.t27 new file mode 100644 index 0000000000..cf9df8fea4 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-ci.t27 @@ -0,0 +1,26 @@ +// 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 SUMMARY_RU : str = "Раз в день прогоняет весь конвейер сборки, проверки типов и тестов, чтобы зависимость, гниющая без пуша, всё равно была поймана."; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-crmProactive-L375.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-crmProactive-L375.t27 new file mode 100644 index 0000000000..84520adc92 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-crmProactive-L375.t27 @@ -0,0 +1,27 @@ +// 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."; +pub const SUMMARY_RU : str = "Один проактивный ход продавца для владельца: обходит очередь CRM и кладёт перед ним следующую карточку-черновик без просьбы."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-daily-sales-advisor.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-daily-sales-advisor.t27 new file mode 100644 index 0000000000..40a00c698e --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-daily-sales-advisor.t27 @@ -0,0 +1,26 @@ +// 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 SUMMARY_RU : str = "Собирает каждому владельцу бота утренний отчёт: вчерашняя выручка, расходы недели и что реально покупали пользователи."; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-generate-jobs-L164.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-generate-jobs-L164.t27 new file mode 100644 index 0000000000..556bcbd47c --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-generate-jobs-L164.t27 @@ -0,0 +1,26 @@ +// 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."; +pub const SUMMARY_RU : str = "Через час после завершения выметает из памяти законченные задания генерации, когда потерявший соединение клиент успел забрать результат."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-health-check.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-health-check.t27 new file mode 100644 index 0000000000..d17229d8ff --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-health-check.t27 @@ -0,0 +1,25 @@ +// 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 SUMMARY_RU : str = "Каждые полчаса опрашивает основной API и сервис Inngest и сообщает, кто ответил."; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-log-monitor.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-log-monitor.t27 new file mode 100644 index 0000000000..fd53fd12bd --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-log-monitor.t27 @@ -0,0 +1,25 @@ +// 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 SUMMARY_RU : str = "Раз в день читает логи приложения, поручает модели свести ошибки и предупреждения и отправляет дайджест в 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L318.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L318.t27 new file mode 100644 index 0000000000..c335caed57 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L318.t27 @@ -0,0 +1,27 @@ +// 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."; +pub const SUMMARY_RU : str = "Разгружает очередь уведомлений и доставляет ждущие в ней сообщения Telegram."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L324.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L324.t27 new file mode 100644 index 0000000000..4b042e671f --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-notificationHandler-L324.t27 @@ -0,0 +1,26 @@ +// 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."; +pub const SUMMARY_RU : str = "Удаляет уведомления, настолько старые, что их уже никто не прочитает."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27 new file mode 100644 index 0000000000..453a233c1b --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27 -- cron inngest/999-multibots-telegraf/periodic-webhook-health-check +; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/periodic-webhook-health-check). +; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/webhookHealthGuard.ts#L218. +; 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_periodic_webhook_health_check; + +pub const KIND : str = "cron"; +pub const ID : str = "inngest/999-multibots-telegraf/periodic-webhook-health-check"; +pub const NAME : str = "periodic-webhook-health-check"; +pub const HOST : str = "inngest"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/inngest_app/functions/webhookHealthGuard.ts"; +pub const SUMMARY_EN : str = "Tests both Telegram webhook URLs every hour and sends the admin a critical alarm when neither of them answers."; +pub const SUMMARY_RU : str = "Каждый час проверяет оба URL вебхука Telegram и шлёт админу критическую тревогу, когда не отвечает ни один."; +pub const SCHEDULE : str = "0 * * * *"; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-production-monitor-L80.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-production-monitor-L80.t27 new file mode 100644 index 0000000000..17a5a202ff --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-production-monitor-L80.t27 @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-production-monitor-L80.t27 -- cron timer/999-multibots-telegraf/production-monitor-L80 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/production-monitor-L80). +; The schedule was read from 999-multibots-telegraf:src/utils/production-monitor.ts#L80. +; 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_production_monitor_L80; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/production-monitor-L80"; +pub const NAME : str = "production-monitor-L80"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/utils/production-monitor.ts:80"; +pub const SUMMARY_EN : str = "Re-runs the container, nginx, port, domain, webhook and system-resource checks, and applies the fixes that are enabled."; +pub const SUMMARY_RU : str = "Повторяет проверки контейнера, nginx, порта, домена, вебхука и ресурсов системы и применяет включённые исправления."; +; 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"; +; Started only by hand (`monitor` argument); the bot's entry point never starts it. +pub const ENABLED : bool = false; +pub const NOTE : str = "Started only by running this module from the command line with the `monitor` argument; the bot's own entry point never starts it."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27 new file mode 100644 index 0000000000..b9c9fcdf0d --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27 @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27 -- cron timer/999-multibots-telegraf/provider-health-monitor-L344 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/provider-health-monitor-L344). +; The schedule was read from 999-multibots-telegraf:src/services/provider-health-monitor.ts#L344. +; 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_provider_health_monitor_L344; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/provider-health-monitor-L344"; +pub const NAME : str = "provider-health-monitor-L344"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/services/provider-health-monitor.ts:344"; +pub const SUMMARY_EN : str = "Checks every AI provider that is not parked and records whether it is currently answering."; +pub const SUMMARY_RU : str = "Проверяет каждого не отключённого AI-провайдера и записывает, отвечает ли он сейчас."; +; 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 NOTE : str = "Providers listed in HEALTH_SKIP_PROVIDERS are deliberately not watched and do not raise the alarm."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L10845.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L10845.t27 new file mode 100644 index 0000000000..ffc4d4aafb --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L10845.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-render-server-L10845.t27 -- cron timer/999-multibots-telegraf/render-server-L10845 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/render-server-L10845). +; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/render-server.ts#L10845. +; 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_render_server_L10845; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/render-server-L10845"; +pub const NAME : str = "render-server-L10845"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "apps/vibee-editor/render/render-server.ts:10845"; +pub const SUMMARY_EN : str = "Pings every websocket peer and drops the ones that did not answer the previous round."; +pub const SUMMARY_RU : str = "Пингует каждого websocket-пира и отключает тех, кто не ответил в прошлом раунде."; +; A setInterval inside a long-lived process: a period, not a cron expression. +pub const INTERVAL_MS : u32 = 30000; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2301.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2301.t27 new file mode 100644 index 0000000000..7f318d2783 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2301.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-render-server-L2301.t27 -- cron timer/999-multibots-telegraf/render-server-L2301 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/render-server-L2301). +; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/render-server.ts#L2301. +; 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_render_server_L2301; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/render-server-L2301"; +pub const NAME : str = "render-server-L2301"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "apps/vibee-editor/render/render-server.ts:2301"; +pub const SUMMARY_EN : str = "Removes render jobs older than an hour from the in-memory job map."; +pub const SUMMARY_RU : str = "Удаляет из карты в памяти задания рендера старше часа."; +; 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 ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2325.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2325.t27 new file mode 100644 index 0000000000..f6c3165d0b --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L2325.t27 @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-render-server-L2325.t27 -- cron timer/999-multibots-telegraf/render-server-L2325 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/render-server-L2325). +; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/render-server.ts#L2325. +; 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_render_server_L2325; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/render-server-L2325"; +pub const NAME : str = "render-server-L2325"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "apps/vibee-editor/render/render-server.ts:2325"; +pub const SUMMARY_EN : str = "Reads the hive journal and delivers the queen's report to the keepers, passing alarms straight through and batching ordinary events."; +pub const SUMMARY_RU : str = "Читает журнал улья и доставляет отчёт королевы хранителям: тревоги — сразу, обычные события — пакетом."; +; A setInterval inside a long-lived process: a period, not a cron expression. +pub const INTERVAL_MS : u32 = 600000; +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 = "Checks often and writes rarely: the cursor lives in the database, because this Railway project has no volume and a deploy wipes local state."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L919.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L919.t27 new file mode 100644 index 0000000000..20f415d1c3 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-render-server-L919.t27 @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-render-server-L919.t27 -- cron timer/999-multibots-telegraf/render-server-L919 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/render-server-L919). +; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/render-server.ts#L919. +; 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_render_server_L919; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/render-server-L919"; +pub const NAME : str = "render-server-L919"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "apps/vibee-editor/render/render-server.ts:919"; +pub const SUMMARY_EN : str = "Re-reads the session revocation list from Postgres so a revoked browser session stops being accepted."; +pub const SUMMARY_RU : str = "Перечитывает из Postgres список отозванных сессий, чтобы отозванная сессия браузера перестала приниматься."; +; A setInterval inside a long-lived process: a period, not a cron expression. +pub const INTERVAL_MS : u32 = 5000; +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 = "Only starts when a session signing key is configured; a deployed render server refuses to boot without one."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-security.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-security.t27 new file mode 100644 index 0000000000..15e9126f42 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-security.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-security.t27 -- cron github-actions/999-multibots-telegraf/security +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/999-multibots-telegraf/security). +; The schedule was read from 999-multibots-telegraf:.github/workflows/security.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_security; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/999-multibots-telegraf/security"; +pub const NAME : str = "Security Checks"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = ".github/workflows/security.yml"; +pub const SUMMARY_EN : str = "Runs the weekly security sweep: secret detection over the full history, dependency audit and code scanning."; +pub const SUMMARY_RU : str = "Еженедельная проверка безопасности: поиск секретов по всей истории, аудит зависимостей и сканирование кода."; +pub const SCHEDULE : str = "0 3 * * 0"; +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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-skill-detector.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-skill-detector.t27 new file mode 100644 index 0000000000..8cc0da8663 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-skill-detector.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-skill-detector.t27 -- cron inngest/999-multibots-telegraf/skill-detector +; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/skill-detector). +; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/analytics/skillDetector.ts#L32. +; 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_skill_detector; + +pub const KIND : str = "cron"; +pub const ID : str = "inngest/999-multibots-telegraf/skill-detector"; +pub const NAME : str = "skill-detector"; +pub const HOST : str = "inngest"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/inngest_app/functions/analytics/skillDetector.ts"; +pub const SUMMARY_EN : str = "Mines successful generations for repeated model and prompt patterns once a day and saves the new ones as reusable skills."; +pub const SUMMARY_RU : str = "Раз в день ищет в успешных генерациях повторяющиеся паттерны моделей и промптов и сохраняет новые как переиспользуемые навыки бота."; +pub const SCHEDULE : str = "0 10 * * *"; +pub const TZ : str = "UTC"; +pub const RUNS : [0]str = []; +pub const RUNS_NOTE : str = "the \"skills\" it saves are bot prompt patterns, not Claude Code skills; no skill invocation found in source"; +pub const ENABLED : bool = true; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "inngest-dashboard"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-taskCache-L27.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-taskCache-L27.t27 new file mode 100644 index 0000000000..a09260d2fd --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-taskCache-L27.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-taskCache-L27.t27 -- cron timer/999-multibots-telegraf/taskCache-L27 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/taskCache-L27). +; The schedule was read from 999-multibots-telegraf:src/modules/videoGenerator/taskCache.ts#L27. +; 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_taskCache_L27; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/taskCache-L27"; +pub const NAME : str = "taskCache-L27"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/modules/videoGenerator/taskCache.ts:27"; +pub const SUMMARY_EN : str = "Drops video-generation cache entries past their fifteen-minute lifetime so a finished task cannot block the next request."; +pub const SUMMARY_RU : str = "Сбрасывает записи кэша видеогенерации старше пятнадцати минут, чтобы завершённая задача не блокировала следующий запрос."; +; 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"; diff --git a/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-video-task-store-L156.t27 b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-video-task-store-L156.t27 new file mode 100644 index 0000000000..355b11c540 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/999-multibots-telegraf-video-task-store-L156.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/999-multibots-telegraf-video-task-store-L156.t27 -- cron timer/999-multibots-telegraf/video-task-store-L156 +; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/video-task-store-L156). +; The schedule was read from 999-multibots-telegraf:src/services/video-task-store.ts#L156. +; 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_video_task_store_L156; + +pub const KIND : str = "cron"; +pub const ID : str = "timer/999-multibots-telegraf/video-task-store-L156"; +pub const NAME : str = "video-task-store-L156"; +pub const HOST : str = "timer"; +pub const REPO : str = "999-multibots-telegraf"; +pub const SERVICE : str = "src/services/video-task-store.ts:156"; +pub const SUMMARY_EN : str = "Forgets video tasks older than an hour and rewrites the store to disk when anything was removed."; +pub const SUMMARY_RU : str = "Забывает видеозадачи старше часа и переписывает хранилище на диск, если что-то было удалено."; +; A setInterval inside a long-lived process: a period, not a cron expression. +pub const INTERVAL_MS : u32 = 600000; +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"; diff --git a/apps/website/public/t27/files/specs/crons/README.md b/apps/website/public/t27/files/specs/crons/README.md new file mode 100644 index 0000000000..b9455b2b49 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/README.md @@ -0,0 +1,41 @@ +# specs/crons — scheduled jobs as first-class `.t27` specs + +One file per scheduled job the site publishes (`apps/website/public/crons/manifest.json`): +GitHub Actions schedules, Inngest functions, Railway cron services and in-process +`setInterval` timers. The `.t27` file is the source of truth for the job card on +t27.ai; `public/crons/spec-crons.json` is generated from it by the real compiler. + +File name: `-.t27`; module name: `cron_`. + +## Schema (every constant is `pub const`) + +| constant | type | meaning | +|-----------------|----------|-------------------------------------------------------------------------------------------| +| `KIND` | `str` | always `"cron"` | +| `ID` | `str` | `//`; must equal the id in the code manifest | +| `NAME` | `str` | display name | +| `HOST` | `str` | `github-actions` \| `inngest` \| `railway-cron` \| `timer` | +| `REPO` | `str` | short repo name | +| `SERVICE` | `str` | workflow file / Inngest function file / Railway service / `file:line` of the timer | +| `SUMMARY_EN` | `str` | what the job does, English | +| `SUMMARY_RU` | `str` | the same, Russian | +| `SCHEDULE` | `str` | cron expression (absent for timers; `""` when only a dashboard holds it, see `SCHEDULE_NOTE`) | +| `INTERVAL_MS` | `u32` | period of an in-process timer (timers only, instead of `SCHEDULE`) | +| `TZ` | `str` | timezone of the schedule | +| `RUNS` | `[N]str` | skill IDs this job launches — **the control link**; only when the source evidently invokes one | +| `RUNS_NOTE` | `str` | why `RUNS` is what it is (e.g. `"no skill invocation found in source"`) | +| `ENABLED` | `bool` | whether the job is meant to fire | +| `NOTE` | `str` | optional caveat carried from the code catalog | +| `ON_FAILURE` | `str` | `issue` \| `log` \| `unknown` | +| `CONTROL` | `str` | `github-actions-dispatch` \| `railway-dashboard` \| `inngest-dashboard` \| `code-only` | + +## Rules the generator enforces + +- duplicate `ID` → build fails; +- a `RUNS` entry that names no skill spec → the job is marked `health: "fail"`; +- an `ID` absent from the code manifest → `witness: "spec-only"` (warn); +- every file must pass `typecheck.ok === true` with nothing discarded by the compiler. + +As of the first commit every `RUNS` is empty: none of the 33 jobs was found to +invoke a Claude Code skill by name (checked in the workflow files and the code +catalog annotations). The link exists so that the day a job does, the spec says so. diff --git a/apps/website/public/t27/files/specs/crons/railway-jobsearch-cron.t27 b/apps/website/public/t27/files/specs/crons/railway-jobsearch-cron.t27 new file mode 100644 index 0000000000..4dec82f0a9 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/railway-jobsearch-cron.t27 @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/railway-jobsearch-cron.t27 -- cron railway-cron/999/jobsearch-cron +; Source of truth for the job card on t27.ai (#/crons?cron=railway-cron/999/jobsearch-cron). +; The schedule lives in the Railway dashboard of project 999 and is not in any checkout. +; 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_railway_jobsearch_cron; + +pub const KIND : str = "cron"; +pub const ID : str = "railway-cron/999/jobsearch-cron"; +pub const NAME : str = "jobsearch-cron"; +pub const HOST : str = "railway-cron"; +pub const REPO : str = "railway"; +pub const SERVICE : str = "jobsearch-cron"; +pub const SUMMARY_EN : str = "The scheduled job-search service of the Railway project, reachable at cron.t27.ai."; +pub const SUMMARY_RU : str = "Сервис поиска вакансий по расписанию в проекте Railway, доступен на cron.t27.ai."; +; No expression is readable from a checkout; the dashboard holds it. +pub const SCHEDULE : str = ""; +pub const SCHEDULE_NOTE : str = "configured in the Railway dashboard (project 999); not readable from the checkout"; +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 schedule is configured in the Railway dashboard for project 999 and is not readable from the checkout; the probe below is what this catalog can verify."; +pub const ON_FAILURE : str = "unknown"; +pub const CONTROL : str = "railway-dashboard"; diff --git a/apps/website/public/t27/files/specs/crons/t27-formal-mutation.t27 b/apps/website/public/t27/files/specs/crons/t27-formal-mutation.t27 new file mode 100644 index 0000000000..d968d25aba --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/t27-formal-mutation.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/t27-formal-mutation.t27 -- cron github-actions/t27/formal-mutation +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/t27/formal-mutation). +; The schedule was read from t27:.github/workflows/formal-mutation.yml#L25. +; 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_t27_formal_mutation; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/t27/formal-mutation"; +pub const NAME : str = "formal-mutation"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "t27"; +pub const SERVICE : str = ".github/workflows/formal-mutation.yml"; +pub const SUMMARY_EN : str = "Proves the formal gates actually bite by mutating the generated RTL once a week and requiring each gate to go red for its own mutation."; +pub const SUMMARY_RU : str = "Раз в неделю мутирует сгенерированный RTL и требует, чтобы каждый формальный гейт покраснел на своей мутации — доказательство, что гейты кусаются."; +pub const SCHEDULE : str = "0 5 * * 1"; +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 = "Weekly rather than per-push because a full run costs about thirty yosys invocations."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/t27-pr-dashboard.t27 b/apps/website/public/t27/files/specs/crons/t27-pr-dashboard.t27 new file mode 100644 index 0000000000..a08fff340c --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/t27-pr-dashboard.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/t27-pr-dashboard.t27 -- cron github-actions/t27/pr-dashboard +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/t27/pr-dashboard). +; The schedule was read from t27:.github/workflows/pr-dashboard.yml#L6. +; 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_t27_pr_dashboard; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/t27/pr-dashboard"; +pub const NAME : str = "PR Dashboard"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "t27"; +pub const SERVICE : str = ".github/workflows/pr-dashboard.yml"; +pub const SUMMARY_EN : str = "Rebuilds the pull-request dashboard every hour from the current state of the open pull requests."; +pub const SUMMARY_RU : str = "Каждый час пересобирает панель pull request из текущего состояния открытых PR."; +pub const SCHEDULE : str = "0 * * * *"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-agent-cron-cleanup.t27 b/apps/website/public/t27/files/specs/crons/trinity-agent-cron-cleanup.t27 new file mode 100644 index 0000000000..68add8f697 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-agent-cron-cleanup.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-agent-cron-cleanup.t27 -- cron github-actions/trinity/agent-cron-cleanup +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/agent-cron-cleanup). +; The schedule was read from trinity:.github/workflows/agent-cron-cleanup.yml#L4. +; 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_trinity_agent_cron_cleanup; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/agent-cron-cleanup"; +pub const NAME : str = "Trinity Agent Cron Cleanup"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/agent-cron-cleanup.yml"; +pub const SUMMARY_EN : str = "Finds agent deployments that got stuck and kills them, every two hours."; +pub const SUMMARY_RU : str = "Каждые два часа находит застрявшие деплои агентов и убивает их."; +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 ON_FAILURE : str = "log"; +pub const CONTROL : str = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-agent-queue-drain.t27 b/apps/website/public/t27/files/specs/crons/trinity-agent-queue-drain.t27 new file mode 100644 index 0000000000..09fceaf69d --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-agent-queue-drain.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-agent-queue-drain.t27 -- cron github-actions/trinity/agent-queue-drain +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/agent-queue-drain). +; The schedule was read from trinity:.github/workflows/agent-queue-drain.yml#L4. +; 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_trinity_agent_queue_drain; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/agent-queue-drain"; +pub const NAME : str = "Trinity Agent Queue Drain"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/agent-queue-drain.yml"; +pub const SUMMARY_EN : str = "Looks for queued issues and free agent slots every five minutes and starts the next agent when there is room."; +pub const SUMMARY_RU : str = "Каждые пять минут ищет задачи в очереди и свободные слоты агентов и запускает следующего агента, когда есть место."; +pub const SCHEDULE : str = "*/5 * * * *"; +pub const TZ : str = "UTC"; +pub const RUNS : [0]str = []; +pub const RUNS_NOTE : str = "dispatches agent-spawn-pool.yml for a queued issue; no named skill invocation found in source"; +pub const ENABLED : bool = true; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-brain-ci.t27 b/apps/website/public/t27/files/specs/crons/trinity-brain-ci.t27 new file mode 100644 index 0000000000..068c54ed59 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-brain-ci.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-brain-ci.t27 -- cron github-actions/trinity/brain-ci +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/brain-ci). +; The schedule was read from trinity:.github/workflows/brain-ci.yml#L11. +; 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_trinity_brain_ci; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/brain-ci"; +pub const NAME : str = "S³AI Brain CI — \"Functional MRI\" Gate"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/brain-ci.yml"; +pub const SUMMARY_EN : str = "Runs the weekly brain stress test, the long version of the health gate that every commit already gets."; +pub const SUMMARY_RU : str = "Еженедельный стресс-тест мозга — длинная версия гейта здоровья, который получает каждый коммит."; +pub const SCHEDULE : str = "0 0 * * 0"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-codegen.t27 b/apps/website/public/t27/files/specs/crons/trinity-codegen.t27 new file mode 100644 index 0000000000..ef2d5ced4b --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-codegen.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-codegen.t27 -- cron github-actions/trinity/codegen +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/codegen). +; The schedule was read from trinity:.github/workflows/codegen.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_trinity_codegen; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/codegen"; +pub const NAME : str = "Codegen Validation"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/codegen.yml"; +pub const SUMMARY_EN : str = "Validates the VIBEE code generator once a day, independently of whether anyone pushed."; +pub const SUMMARY_RU : str = "Раз в день проверяет генератор кода VIBEE независимо от того, пушил ли кто-нибудь."; +pub const SCHEDULE : str = "0 0 * * *"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-discover-callers.t27 b/apps/website/public/t27/files/specs/crons/trinity-discover-callers.t27 new file mode 100644 index 0000000000..16eb0eafd0 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-discover-callers.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-discover-callers.t27 -- cron github-actions/trinity/discover-callers +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/discover-callers). +; The schedule was read from trinity:.github/workflows/discover-callers.yml#L16. +; 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_trinity_discover_callers; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/discover-callers"; +pub const NAME : str = "Discover RTL check callers"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/discover-callers.yml"; +pub const SUMMARY_EN : str = "Reads GitHub for repositories that run the RTL check, so the gallery counts its own users instead of being hand-written."; +pub const SUMMARY_RU : str = "Читает GitHub в поисках репозиториев, запускающих RTL-проверку, чтобы галерея считала своих пользователей сама."; +pub const SCHEDULE : str = "41 5 * * *"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-pages-health-check.t27 b/apps/website/public/t27/files/specs/crons/trinity-pages-health-check.t27 new file mode 100644 index 0000000000..3f80cfe11f --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-pages-health-check.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-pages-health-check.t27 -- cron github-actions/trinity/pages-health-check +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/pages-health-check). +; The schedule was read from trinity:.github/workflows/pages-health-check.yml#L17. +; 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_trinity_pages_health_check; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/pages-health-check"; +pub const NAME : str = "Pages Health Check"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/pages-health-check.yml"; +pub const SUMMARY_EN : str = "Fetches the published site every fifteen minutes, follows the redirect, and redeploys only when the evidence says the deploy is what broke."; +pub const SUMMARY_RU : str = "Каждые пятнадцать минут запрашивает опубликованный сайт, следует редиректу и передеплоит только когда свидетельства говорят, что сломан именно деплой."; +pub const SCHEDULE : str = "*/15 * * * *"; +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 = "An earlier version judged a bare status code, failed ninety-six times a day on a site that was up, and dispatched a redeploy each time."; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-signal-health-self.t27 b/apps/website/public/t27/files/specs/crons/trinity-signal-health-self.t27 new file mode 100644 index 0000000000..2888efaec8 --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-signal-health-self.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-signal-health-self.t27 -- cron github-actions/trinity/signal-health-self +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/signal-health-self). +; The schedule was read from trinity:.github/workflows/signal-health-self.yml#L17. +; 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_trinity_signal_health_self; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/signal-health-self"; +pub const NAME : str = "Signal health (self)"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/signal-health-self.yml"; +pub const SUMMARY_EN : str = "Applies the signal-health check to the repository that sells it and writes the numbers into the website data."; +pub const SUMMARY_RU : str = "Применяет проверку signal-health к репозиторию, который её продаёт, и записывает числа в данные сайта."; +pub const SCHEDULE : str = "23 4 * * *"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/crons/trinity-site-live-gate.t27 b/apps/website/public/t27/files/specs/crons/trinity-site-live-gate.t27 new file mode 100644 index 0000000000..100f09d2df --- /dev/null +++ b/apps/website/public/t27/files/specs/crons/trinity-site-live-gate.t27 @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/crons/trinity-site-live-gate.t27 -- cron github-actions/trinity/site-live-gate +; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/trinity/site-live-gate). +; The schedule was read from trinity:.github/workflows/site-live-gate.yml#L33. +; 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_trinity_site_live_gate; + +pub const KIND : str = "cron"; +pub const ID : str = "github-actions/trinity/site-live-gate"; +pub const NAME : str = "Site live gate"; +pub const HOST : str = "github-actions"; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/site-live-gate.yml"; +pub const SUMMARY_EN : str = "Compares the content-hashed entry bundle served at the apex against a build of main, so that 'deployed' means a reader can actually see it."; +pub const SUMMARY_RU : str = "Сравнивает бандл с контент-хешем на апексе со сборкой main, чтобы «задеплоено» означало, что читатель это видит."; +pub const SCHEDULE : str = "17 6 * * *"; +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 = "github-actions-dispatch"; diff --git a/apps/website/public/t27/files/specs/skills/README.md b/apps/website/public/t27/files/specs/skills/README.md new file mode 100644 index 0000000000..94afb35fb2 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/README.md @@ -0,0 +1,38 @@ +# specs/skills — skills as first-class `.t27` specs + +One file per Claude Code skill the site publishes (`apps/website/public/skills/manifest.json`). +The `.t27` file is the source of truth for the skill card on t27.ai; the JSON the site +serves (`public/skills/spec-skills.json`) is generated from these files by the real +compiler (`t27_compiler.wasm`), never by a regex. + +File name: `-.t27`; module name: `skill_`. + +## Schema (every constant is `pub const`) + +| constant | type | meaning | +|---------------|-----------|-------------------------------------------------------------------------| +| `KIND` | `str` | always `"skill"` | +| `ID` | `str` | `/`; must equal the id in the code manifest | +| `NAME` | `str` | display name (frontmatter `name`, else the directory) | +| `REPO` | `str` | short repo name (`t27`, `trinity`, …) | +| `SOURCE` | `str` | the skill file inside its directory (`SKILL.md` / `skill.md`) | +| `SUMMARY_EN` | `str` | one-paragraph summary, English | +| `SUMMARY_RU` | `str` | the same, Russian | +| `COMMAND` | `str` | how it is invoked (`/name argument-hint`); `""` when nothing is declared | +| `SPECS` | `[N]str` | `.t27` paths the skill stands on; `[0]str = []` when none is declared | +| `TAGS` | `[N]str` | short topical tags | +| `ENABLED` | `bool` | whether the skill is offered | +| `TIMEOUT_MIN` | `u16` | budget in minutes (30 = default, nothing declared in SKILL.md) | + +## Witness labels the site derives + +- `spec+code` — a spec here **and** the skill exists in the code manifest. +- `spec-only` — a spec here with no matching skill in code (warn). +- `code-only` — a skill in code with no spec here (listed, not invented). + +A cron spec (`../crons/`) names the skills it launches in its `RUNS`; the site +shows that link in both directions (`runBy` on the skill card). + +Validity: every file must pass `typecheck.ok === true` with empty +`discarded` / `lexerDiscarded` / `swallowed` under the vendored wasm compiler — +the site's `scripts/agents-from-specs.mjs` fails the build otherwise. diff --git a/apps/website/public/t27/files/specs/skills/t27-measure-corpus.t27 b/apps/website/public/t27/files/specs/skills/t27-measure-corpus.t27 new file mode 100644 index 0000000000..00b2456d57 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-measure-corpus.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-measure-corpus.t27 -- skill t27/measure-corpus +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/measure-corpus). +; The skill body lives in gHashTag/t27 at .claude/skills/measure-corpus/SKILL.md +; (vendored copy: apps/website/public/skills/files/t27/measure-corpus/SKILL.md, sha256 b8fb7503f660...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_measure_corpus; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/measure-corpus"; +pub const NAME : str = "MEASURE THE CORPUS"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Pick the right instrument for a claim about the t27 spec corpus, and avoid the specific traps that have produced wrong published numbers. Use before quoting any figure about specs, errors, or gates."; +pub const SUMMARY_RU : str = "Выбор правильного инструмента для любого утверждения о корпусе спек t27 и список ловушек, которые уже давали неверные опубликованные числа. Использовать перед тем, как цитировать любое число о корпусе."; +; The frontmatter name is a display title, not a slash command, so no invocation is declared. +pub const COMMAND : str = ""; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["corpus", "measurement", "honesty"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/t27-phi-loop.t27 b/apps/website/public/t27/files/specs/skills/t27-phi-loop.t27 new file mode 100644 index 0000000000..e705336d01 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-phi-loop.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-phi-loop.t27 -- skill t27/phi-loop +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/phi-loop). +; The skill body lives in gHashTag/t27 at .claude/skills/phi-loop/SKILL.md +; (vendored copy: apps/website/public/skills/files/t27/phi-loop/SKILL.md, sha256 d8af7f6a9cbc...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_phi_loop; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/phi-loop"; +pub const NAME : str = "PHI LOOP"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Execute 9-phase spec-first development workflow (Issue → Spec → TDD → Code → Gen → Seal → Verify → Land → Learn)"; +pub const SUMMARY_RU : str = "Девятифазный цикл spec-first разработки: Issue → Spec → TDD → Code → Gen → Seal → Verify → Land → Learn."; +; The frontmatter name is a display title, not a slash command, so no invocation is declared. +pub const COMMAND : str = ""; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["workflow", "spec-first", "tdd"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/t27-tri-pipeline.t27 b/apps/website/public/t27/files/specs/skills/t27-tri-pipeline.t27 new file mode 100644 index 0000000000..45359e9f47 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-tri-pipeline.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-tri-pipeline.t27 -- skill t27/tri-pipeline +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/tri-pipeline). +; The skill body lives in gHashTag/t27 at .claude/skills/tri-pipeline/SKILL.md +; (vendored copy: apps/website/public/skills/files/t27/tri-pipeline/SKILL.md, sha256 b4bfc290b86a...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_tri_pipeline; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/tri-pipeline"; +pub const NAME : str = "TRI Pipeline"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Execute tri commands (gen, test, verify, seal, verdict) for spec-first development"; +pub const SUMMARY_RU : str = "Выполнение команд tri (gen, test, verify, seal, verdict) для spec-first разработки."; +; The frontmatter name is a display title, not a slash command, so no invocation is declared. +pub const COMMAND : str = ""; +; Specs this skill stands on, as declared (frontmatter or the site's bindings.json). +pub const SPECS : [1]str = ["specs/pipeline/e2e_test.t27"]; +pub const TAGS : [3]str = ["tri", "pipeline", "verify"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/t27-tri.t27 b/apps/website/public/t27/files/specs/skills/t27-tri.t27 new file mode 100644 index 0000000000..109a1c0539 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-tri.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-tri.t27 -- skill t27/tri +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/tri). +; The skill body lives in gHashTag/t27 at .claude/skills/tri/skill.md +; (vendored copy: apps/website/public/skills/files/t27/tri/skill.md, sha256 8fb4d4796c2d...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_tri; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/tri"; +pub const NAME : str = "tri"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "skill.md"; +pub const SUMMARY_EN : str = "This skill should be used when user asks to \"tri skill begin\", \"PHI LOOP\", \"edit .t27 spec\", \"seal hash\", \"tri gen\", \"tri test\", \"tri verdict\", \"tri experience save\", \"tri notebook query\", \"tri notebook wrapup\", or any task requiring spe..."; +pub const SUMMARY_RU : str = "Команды tri для работы со скилом: begin, правка .t27-спеки, seal hash, gen, test, verdict, experience. Реестр скилов описан в compiler/skill/registry.t27."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/tri"; +; Specs this skill stands on, as declared (frontmatter or the site's bindings.json). +pub const SPECS : [1]str = ["compiler/skill/registry.t27"]; +pub const TAGS : [3]str = ["tri", "workflow", "seal"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/t27-wave-audit.t27 b/apps/website/public/t27/files/specs/skills/t27-wave-audit.t27 new file mode 100644 index 0000000000..69d342bfc4 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-wave-audit.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-wave-audit.t27 -- skill t27/wave-audit +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/wave-audit). +; The skill body lives in gHashTag/t27 at .claude/skills/wave-audit/SKILL.md +; (vendored copy: apps/website/public/skills/files/t27/wave-audit/SKILL.md, sha256 4cd0dd22105a...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_wave_audit; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/wave-audit"; +pub const NAME : str = "Wave Audit — Comprehensive Project Analysis"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Full-spectrum audit of t27 project: GitHub issues analysis, weakness identification, SOTA research review, decomposed plan, and implementation of critical fixes."; +pub const SUMMARY_RU : str = "Полный аудит проекта t27: разбор GitHub issues, поиск слабых мест, обзор SOTA, декомпозированный план и реализация критичных исправлений."; +; The skill file has no frontmatter, so it declares no invocation. +pub const COMMAND : str = ""; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["audit", "issues", "plan"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/t27-wrap-up.t27 b/apps/website/public/t27/files/specs/skills/t27-wrap-up.t27 new file mode 100644 index 0000000000..1761ce6dae --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/t27-wrap-up.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/t27-wrap-up.t27 -- skill t27/wrap-up +; Source of truth for the skill card on t27.ai (#/skills?skill=t27/wrap-up). +; The skill body lives in gHashTag/t27 at .claude/skills/wrap-up/skill.md +; (vendored copy: apps/website/public/skills/files/t27/wrap-up/skill.md, sha256 a4a9b4fd9d02...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_t27_wrap_up; + +pub const KIND : str = "skill"; +pub const ID : str = "t27/wrap-up"; +pub const NAME : str = "wrap-up"; +pub const REPO : str = "t27"; +pub const SOURCE : str = "skill.md"; +pub const SUMMARY_EN : str = "Format and upload session wrap-up to NotebookLM for persistent semantic memory"; +pub const SUMMARY_RU : str = "Оформление и загрузка итогов сессии в NotebookLM как постоянной семантической памяти."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/wrap-up"; +; Specs this skill stands on, as declared (frontmatter or the site's bindings.json). +pub const SPECS : [1]str = ["specs/automation/wrapup-auto.t27"]; +pub const TAGS : [3]str = ["memory", "notebooklm", "session"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-blog-post.t27 b/apps/website/public/t27/files/specs/skills/trinity-blog-post.t27 new file mode 100644 index 0000000000..84b54f3ee6 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-blog-post.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-blog-post.t27 -- skill trinity/blog-post +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/blog-post). +; The skill body lives in gHashTag/trinity at .claude/skills/blog-post/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/blog-post/SKILL.md, sha256 2cb755fd4a60...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_blog_post; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/blog-post"; +pub const NAME : str = "blog-post"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Write and publish a post to the t27.ai blog. Covers where the blog actually lives (not where it looks like it lives), the Post schema with its required receipts and openQuestions, the honesty rules the data file itself imposes, and the b..."; +pub const SUMMARY_RU : str = "Написать и опубликовать пост в блог t27.ai: где блог реально живёт, схема Post с обязательными свидетельствами, обложка и проверки."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/blog-post"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["blog", "publish", "website"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-board-sync.t27 b/apps/website/public/t27/files/specs/skills/trinity-board-sync.t27 new file mode 100644 index 0000000000..729d0b58fc --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-board-sync.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-board-sync.t27 -- skill trinity/board-sync +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/board-sync). +; The skill body lives in gHashTag/trinity at .claude/skills/board-sync/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/board-sync/SKILL.md, sha256 f4bfa63737cf...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_board_sync; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/board-sync"; +pub const NAME : str = "board"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "GitHub Project Board auto-sync — fill empty fields, sync labels with board status, audit field completeness. Keeps board as realtime source of truth."; +pub const SUMMARY_RU : str = "Автосинхронизация GitHub Project Board: заполнить пустые поля, согласовать метки со статусом доски, проверить полноту полей."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/board [sync|audit|fix]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["github", "board", "labels"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-cloud.t27 b/apps/website/public/t27/files/specs/skills/trinity-cloud.t27 new file mode 100644 index 0000000000..d1a69e3e4d --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-cloud.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-cloud.t27 -- skill trinity/cloud +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/cloud). +; The skill body lives in gHashTag/trinity at .claude/skills/cloud/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/cloud/SKILL.md, sha256 53aa338dddc4...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_cloud; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/cloud"; +pub const NAME : str = "cloud"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Show Trinity Cloud Dev dashboard — containers, training farm, issues, PRs, costs. Use when checking agent swarm status, cloud containers, or CI/CD pipeline."; +pub const SUMMARY_RU : str = "Панель Trinity Cloud Dev: контейнеры, тренировочная ферма, issues, PR, расходы. Для проверки состояния роя агентов и CI/CD."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/cloud [status|agents|events|full]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["dashboard", "railway", "agents"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-doctor.t27 b/apps/website/public/t27/files/specs/skills/trinity-doctor.t27 new file mode 100644 index 0000000000..4261110596 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-doctor.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-doctor.t27 -- skill trinity/doctor +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/doctor). +; The skill body lives in gHashTag/trinity at .claude/skills/doctor/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/doctor/SKILL.md, sha256 68efdf793010...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_doctor; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/doctor"; +pub const NAME : str = "doctor"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "HEALER — diagnose, heal, commit dirty files, monitor junk & docs & duplicates, report honestly. Every loop = action + proof."; +pub const SUMMARY_RU : str = "ЛЕКАРЬ: диагностика, лечение, коммит грязных файлов, надзор за мусором, документацией и дубликатами, честный отчёт. Каждый цикл = действие + доказательство."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/doctor [quick|full|scan|junk|docs|dupes] [lang:ru|en]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["health", "repo", "report"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-farm-garden.t27 b/apps/website/public/t27/files/specs/skills/trinity-farm-garden.t27 new file mode 100644 index 0000000000..79a039201e --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-farm-garden.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-farm-garden.t27 -- skill trinity/farm-garden +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/farm-garden). +; The skill body lives in gHashTag/trinity at .claude/skills/farm-garden/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/farm-garden/SKILL.md, sha256 672148988575...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_farm_garden; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/farm-garden"; +pub const NAME : str = "Garden — Quantum Gardener for Trinity Farm"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Autonomous farm population manager. Monitors, evolves, and recycles HSLM training workers."; +pub const SUMMARY_RU : str = "Автономный управляющий популяцией фермы: наблюдает, эволюционирует и перерабатывает рабочих обучения HSLM."; +; The skill file has no frontmatter, so it declares no invocation. +pub const COMMAND : str = ""; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["farm", "hslm", "workers"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-fpga-synth.t27 b/apps/website/public/t27/files/specs/skills/trinity-fpga-synth.t27 new file mode 100644 index 0000000000..cb3fc93b94 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-fpga-synth.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-fpga-synth.t27 -- skill trinity/fpga-synth +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/fpga-synth). +; The skill body lives in gHashTag/trinity at .claude/skills/fpga-synth/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/fpga-synth/SKILL.md, sha256 de3915e66cda...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_fpga_synth; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/fpga-synth"; +pub const NAME : str = "fpga-synth"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Run FPGA synthesis pipeline from .tri spec to bitstream. Use when working with Verilog, FPGA, or hardware synthesis."; +pub const SUMMARY_RU : str = "Конвейер синтеза FPGA от .tri-спеки до битстрима. Для работы с Verilog, FPGA и аппаратным синтезом."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/fpga-synth "; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["fpga", "verilog", "synthesis"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-god-mode.t27 b/apps/website/public/t27/files/specs/skills/trinity-god-mode.t27 new file mode 100644 index 0000000000..950f503dfd --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-god-mode.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-god-mode.t27 -- skill trinity/god-mode +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/god-mode). +; The skill body lives in gHashTag/trinity at .claude/skills/god-mode/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/god-mode/SKILL.md, sha256 0c48e328ae4c...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_god_mode; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/god-mode"; +pub const NAME : str = "god-mode"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "GOD MODE — Agent monitoring dashboard. Shows swarm status, task queue, pipeline compliance, circuit breakers, git activity, and rule violations."; +pub const SUMMARY_RU : str = "GOD MODE: панель наблюдения за агентами — состояние роя, очередь задач, соблюдение конвейера, предохранители, активность git, нарушения правил."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/god-mode [status|agents|tasks|violations]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["dashboard", "agents", "monitoring"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-implement-issue.t27 b/apps/website/public/t27/files/specs/skills/trinity-implement-issue.t27 new file mode 100644 index 0000000000..4ea3bc849d --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-implement-issue.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-implement-issue.t27 -- skill trinity/implement-issue +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/implement-issue). +; The skill body lives in gHashTag/trinity at .claude/skills/implement-issue/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/implement-issue/SKILL.md, sha256 88ccafaece20...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_implement_issue; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/implement-issue"; +pub const NAME : str = "implement-issue"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Read a GitHub issue, create a branch, implement the solution, and open a PR. Use when given an issue number to implement."; +pub const SUMMARY_RU : str = "Прочитать GitHub issue, создать ветку, реализовать решение и открыть PR. Для работы по номеру задачи."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/implement-issue [issue-number]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["github", "issue", "pr"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-queen-hive-visuals.t27 b/apps/website/public/t27/files/specs/skills/trinity-queen-hive-visuals.t27 new file mode 100644 index 0000000000..dfba6a4db3 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-queen-hive-visuals.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-queen-hive-visuals.t27 -- skill trinity/queen-hive-visuals +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/queen-hive-visuals). +; The skill body lives in gHashTag/trinity at .claude/skills/queen-hive-visuals/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/queen-hive-visuals/SKILL.md, sha256 c6d914dc81c8...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_queen_hive_visuals; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/queen-hive-visuals"; +pub const NAME : str = "queen-hive-visuals"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Maintain QUEEN's floating hive, original point-down TRINITY logo, sharp issue displays and matching camera interactions."; +pub const SUMMARY_RU : str = "Поддержка плавающего улья Королевы, оригинального логотипа TRINITY остриём вниз, чётких табло задач и согласованных движений камеры."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/queen-hive-visuals"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["queen", "ui", "3d"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-review-code.t27 b/apps/website/public/t27/files/specs/skills/trinity-review-code.t27 new file mode 100644 index 0000000000..a92283d24c --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-review-code.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-review-code.t27 -- skill trinity/review-code +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/review-code). +; The skill body lives in gHashTag/trinity at .claude/skills/review-code/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/review-code/SKILL.md, sha256 e5712744615c...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_review_code; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/review-code"; +pub const NAME : str = "review-code"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Review recent code changes for bugs, style issues, and improvements. Use after writing code or before committing."; +pub const SUMMARY_RU : str = "Ревью недавних изменений кода: ошибки, стиль, улучшения. После написания кода или перед коммитом."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/review-code [file-or-branch] (optional)"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [2]str = ["review", "quality"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-run-tests.t27 b/apps/website/public/t27/files/specs/skills/trinity-run-tests.t27 new file mode 100644 index 0000000000..6b84a0e179 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-run-tests.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-run-tests.t27 -- skill trinity/run-tests +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/run-tests). +; The skill body lives in gHashTag/trinity at .claude/skills/run-tests/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/run-tests/SKILL.md, sha256 7ead75ab36b8...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_run_tests; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/run-tests"; +pub const NAME : str = "run-tests"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Build the project, check for warnings, run all tests, and report results. Quick health check for the codebase."; +pub const SUMMARY_RU : str = "Собрать проект, проверить предупреждения, прогнать все тесты и отчитаться. Быстрая проверка здоровья кодовой базы."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/run-tests [module] (optional - all, vsa, vm, bsd, hslm, tri-api)"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["tests", "build", "zig"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-scholar.t27 b/apps/website/public/t27/files/specs/skills/trinity-scholar.t27 new file mode 100644 index 0000000000..6cd23d67a8 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-scholar.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-scholar.t27 -- skill trinity/scholar +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/scholar). +; The skill body lives in gHashTag/trinity at .claude/skills/scholar/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/scholar/SKILL.md, sha256 5c35431008a4...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_scholar; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/scholar"; +pub const NAME : str = "scholar"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Self-Evolving Research Agent — scans web for relevant tech, evaluates findings, proposes improvements. Uses Perplexity Sonar API via MCP."; +pub const SUMMARY_RU : str = "Саморазвивающийся исследовательский агент: сканирует веб на релевантные технологии, оценивает находки, предлагает улучшения. Perplexity Sonar через MCP."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/scholar [scan|eval|apply|full|report|topic:\"query\"]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["research", "perplexity", "mcp"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-status.t27 b/apps/website/public/t27/files/specs/skills/trinity-status.t27 new file mode 100644 index 0000000000..387d31d586 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-status.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-status.t27 -- skill trinity/status +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/status). +; The skill body lives in gHashTag/trinity at .claude/skills/status/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/status/SKILL.md, sha256 e4bc75ff45cd...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_status; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/status"; +pub const NAME : str = "status"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Live system dashboard — 12-dimension ouroboros score, verdict v3, agents, build health, recommendations"; +pub const SUMMARY_RU : str = "Живая панель системы: 12-мерная оценка уробороса, verdict v3, агенты, здоровье сборки, рекомендации."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/status [quick|full] [lang:ru|en]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["dashboard", "ouroboros", "verdict"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-tech-tree.t27 b/apps/website/public/t27/files/specs/skills/trinity-tech-tree.t27 new file mode 100644 index 0000000000..5739db4d2f --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-tech-tree.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-tech-tree.t27 -- skill trinity/tech-tree +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/tech-tree). +; The skill body lives in gHashTag/trinity at .claude/skills/tech-tree/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/tech-tree/SKILL.md, sha256 f84c8ea67ab6...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_tech_tree; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/tech-tree"; +pub const NAME : str = "tree"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Technology dependency tree — shows issue DAG, prereqs, critical path, blocked/ready tasks. Single source of truth for work order."; +pub const SUMMARY_RU : str = "Дерево технологических зависимостей: DAG задач, предпосылки, критический путь, заблокированные и готовые задачи. Единый источник порядка работ."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/tree [next|path|blocked|update]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["issues", "dag", "planning"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-tri.t27 b/apps/website/public/t27/files/specs/skills/trinity-tri.t27 new file mode 100644 index 0000000000..b0c63016a3 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-tri.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-tri.t27 -- skill trinity/tri +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/tri). +; The skill body lives in gHashTag/trinity at .claude/skills/tri/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/tri/SKILL.md, sha256 4c38d1005219...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_tri; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/tri"; +pub const NAME : str = "tri"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "TRI status dashboard — agent thought mode by default, /tri full for visual dashboard"; +pub const SUMMARY_RU : str = "Панель статуса TRI: по умолчанию режим мысли агента, /tri full — визуальная панель."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/tri [short] [full] [audit] [coverage] [lang:ru|en] [humor|prof|toxic|zen]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["tri", "dashboard", "status"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-trinity-test.t27 b/apps/website/public/t27/files/specs/skills/trinity-trinity-test.t27 new file mode 100644 index 0000000000..95ace08cce --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-trinity-test.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-trinity-test.t27 -- skill trinity/trinity-test +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/trinity-test). +; The skill body lives in gHashTag/trinity at .claude/skills/trinity-test/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/trinity-test/SKILL.md, sha256 648c7f095f0b...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_trinity_test; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/trinity-test"; +pub const NAME : str = "trinity-test"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Run Trinity test suites and analyze results. Use for testing VSA, VM, Firebird, WASM, or full test suite."; +pub const SUMMARY_RU : str = "Прогон тестовых наборов Trinity и разбор результатов: VSA, VM, Firebird, WASM или всё целиком."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/trinity-test [module] (vsa, vm, firebird, all)"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["tests", "vsa", "vm"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-ux-wave.t27 b/apps/website/public/t27/files/specs/skills/trinity-ux-wave.t27 new file mode 100644 index 0000000000..d12c0753a8 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-ux-wave.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-ux-wave.t27 -- skill trinity/ux-wave +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/ux-wave). +; The skill body lives in gHashTag/trinity at .claude/skills/ux-wave/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/ux-wave/SKILL.md, sha256 9690999e8d1e...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_ux_wave; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/ux-wave"; +pub const NAME : str = "ux-wave"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Autonomous UX improvement waves — launch 4 parallel agents per wave, audit every 5 waves, commit fixes. For Queen UI chat & network polish."; +pub const SUMMARY_RU : str = "Автономные волны улучшения UX: 4 параллельных агента на волну, аудит каждые 5 волн, коммит исправлений. Для чата и сети Queen UI."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/ux-wave [wave-number] [audit]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["ux", "agents", "queen"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-vibee-gen.t27 b/apps/website/public/t27/files/specs/skills/trinity-vibee-gen.t27 new file mode 100644 index 0000000000..0c369398eb --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-vibee-gen.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-vibee-gen.t27 -- skill trinity/vibee-gen +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/vibee-gen). +; The skill body lives in gHashTag/trinity at .claude/skills/vibee-gen/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/vibee-gen/SKILL.md, sha256 fc01e69f1395...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_vibee_gen; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/vibee-gen"; +pub const NAME : str = "vibee-gen"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Generate Zig or Verilog code from VIBEE specifications. Use when creating or updating .tri specs and regenerating code."; +pub const SUMMARY_RU : str = "Генерация кода Zig или Verilog из спецификаций VIBEE. При создании или обновлении .tri-спек и перегенерации кода."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/vibee-gen "; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["codegen", "zig", "verilog"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-vsa-verify.t27 b/apps/website/public/t27/files/specs/skills/trinity-vsa-verify.t27 new file mode 100644 index 0000000000..a03ea55876 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-vsa-verify.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-vsa-verify.t27 -- skill trinity/vsa-verify +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/vsa-verify). +; The skill body lives in gHashTag/trinity at .claude/skills/vsa-verify/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/vsa-verify/SKILL.md, sha256 8b9c4518b1bc...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_vsa_verify; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/vsa-verify"; +pub const NAME : str = "vsa-verify"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Verify VSA (Vector Symbolic Architecture) mathematical proofs and operations. Use for math verification, bind/unbind/bundle testing, phi identity checks."; +pub const SUMMARY_RU : str = "Проверка математических доказательств и операций VSA: bind/unbind/bundle, тождества phi."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/vsa-verify [operation or proof to verify]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["vsa", "math", "verify"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; diff --git a/apps/website/public/t27/files/specs/skills/trinity-wave.t27 b/apps/website/public/t27/files/specs/skills/trinity-wave.t27 new file mode 100644 index 0000000000..7cf2ea0041 --- /dev/null +++ b/apps/website/public/t27/files/specs/skills/trinity-wave.t27 @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/skills/trinity-wave.t27 -- skill trinity/wave +; Source of truth for the skill card on t27.ai (#/skills?skill=trinity/wave). +; The skill body lives in gHashTag/trinity at .claude/skills/wave/SKILL.md +; (vendored copy: apps/website/public/skills/files/trinity/wave/SKILL.md, sha256 ecf06fdfa467...). +; The JSON the site serves is generated from this file by the real compiler; +; nothing below is parsed with a regex. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module skill_trinity_wave; + +pub const KIND : str = "skill"; +pub const ID : str = "trinity/wave"; +pub const NAME : str = "wave"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "Farm wave management — create Railway accounts, spawn workers, deploy configs, track wave progress. Use for scaling training farm."; +pub const SUMMARY_RU : str = "Управление волнами фермы: аккаунты Railway, запуск рабочих, раскатка конфигов, прогресс волны. Для масштабирования тренировочной фермы."; +; Invocation, from the skill's own frontmatter (name + argument-hint). +pub const COMMAND : str = "/wave [status|spawn |deploy|progress]"; +; No spec is declared for this skill yet; the Skill Explorer lists it as unbound. +pub const SPECS : [0]str = []; +pub const TAGS : [3]str = ["farm", "railway", "scaling"]; +pub const ENABLED : bool = true; +; Default budget; SKILL.md declares no timeout of its own. +pub const TIMEOUT_MIN : u16 = 30; From 5ec9f09c3f6a5d9941e2beaad38ea9a02c585b03 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Wed, 9 Sep 2026 12:05:46 +0000 Subject: [PATCH 2/5] build(agents): generate spec-skills.json / spec-crons.json from the .t27 specs scripts/agents-from-specs.mjs runs every spec through the vendored compiler wasm, checks the field schema (the wasm's typecheck.ok is necessary, not sufficient: it stays true for wrong annotations and out-of-range ints), joins the result with the code manifests and labels each entry spec+code, spec-only or code-only. Duplicate IDs fail the build; an unresolved RUNS id is health:'fail'. Wired into prebuild and prebuild:ci after skills-core index; node --test covers the schema gate and the joins; qa/agents-spec-contract.mjs (npm run check:agents) pins the catalogs to the specs and the Queen modules to HUD_VIEWS. Railway project/service ids join scripts/railway-services.json so 'run now' can resolve at build time. --- apps/website/package.json | 8 +- apps/website/public/crons/spec-crons.json | 1 + apps/website/public/skills/spec-skills.json | 1 + apps/website/qa/agents-spec-contract.mjs | 173 +++++++ apps/website/scripts/agents-from-specs.mjs | 440 ++++++++++++++++++ .../scripts/agents-from-specs.test.mjs | 201 ++++++++ apps/website/scripts/railway-services.json | 2 +- 7 files changed, 822 insertions(+), 4 deletions(-) create mode 100644 apps/website/public/crons/spec-crons.json create mode 100644 apps/website/public/skills/spec-skills.json create mode 100644 apps/website/qa/agents-spec-contract.mjs create mode 100644 apps/website/scripts/agents-from-specs.mjs create mode 100644 apps/website/scripts/agents-from-specs.test.mjs diff --git a/apps/website/package.json b/apps/website/package.json index bf9ecf21e8..6909325b2d 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -14,8 +14,8 @@ "check:spec-catalog": "node --experimental-strip-types qa/spec-catalog-contract.mjs", "core": "node scripts/spec-core.mjs", "check:shared-core": "node --experimental-strip-types qa/shared-spec-core.mjs", - "prebuild": "npm run core -- index && node scripts/skills-core.mjs index", - "prebuild:ci": "npm run core -- index && node scripts/skills-core.mjs index", + "prebuild": "npm run core -- index && node scripts/skills-core.mjs index && node scripts/agents-from-specs.mjs", + "prebuild:ci": "npm run core -- index && node scripts/skills-core.mjs index && node scripts/agents-from-specs.mjs", "dev": "vite", "build": "vite build", "build:ci": "vite build", @@ -64,7 +64,9 @@ "check:clients-console": "node --experimental-strip-types qa/clients-console-contract.mjs", "check:explorer-languages": "node qa/explorer-language-contract.mjs", "test:sync-skills": "node --test scripts/sync-skills.test.mjs scripts/skills-core.test.mjs", - "test:sync-crons": "node --test scripts/sync-crons.test.mjs" + "test:sync-crons": "node --test scripts/sync-crons.test.mjs", + "check:agents": "node --experimental-strip-types qa/agents-spec-contract.mjs", + "test:agents-specs": "node --test scripts/agents-from-specs.test.mjs" }, "dependencies": { "@babylonjs/core": "^9.25.0", diff --git a/apps/website/public/crons/spec-crons.json b/apps/website/public/crons/spec-crons.json new file mode 100644 index 0000000000..5c5bfa7744 --- /dev/null +++ b/apps/website/public/crons/spec-crons.json @@ -0,0 +1 @@ +{"codeOnly":[],"compilerWasmSha256":"4d9c0447b5ca288790ab03d3dffc2dda629ed47af9d6731a105fc923aea0aee4","contentSha256":"2718d01a2cdd4084317427b6627be579bb137894f23479b676771f582bc4df54","counts":{"byHost":{"github-actions":12,"inngest":6,"railway-cron":1,"timer":14},"codeOnly":0,"specOnly":0,"specPlusCode":33,"specs":33,"typecheckOk":33,"withRuns":0},"crons":[{"code":{"health":"warn","kind":"github-actions","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/.github/workflows/ci.yml#L10","where":{"file":".github/workflows/ci.yml","host":"github-actions","line":10,"service":null}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"github-actions","ID":"github-actions/999-multibots-telegraf/ci","KIND":"cron","NAME":"CI/CD Pipeline","NOTE":"GitHub Actions on this repository have not run since 2026-06-02 (billing); the schedule stands in the file but does not fire.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 2 * * *","SERVICE":".github/workflows/ci.yml","SUMMARY_EN":"Runs the whole build, type-check and test pipeline once a day so a dependency that rots without a push still gets caught.","SUMMARY_RU":"Раз в день прогоняет весь конвейер сборки, проверки типов и тестов, чтобы зависимость, гниющая без пуша, всё равно была поймана.","TZ":"UTC"},"health":"ok","id":"github-actions/999-multibots-telegraf/ci","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_ci","runNow":{"kind":"link","url":"https://github.com/gHashTag/999-multibots-telegraf/actions/workflows/ci.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"d733d23850b691f1cdb7690f3e4d4d3c519d5b5b1e26eb1a97e54d4c911cee23","specPath":"specs/crons/999-multibots-telegraf-ci.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","kind":"github-actions","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/.github/workflows/security.yml#L10","where":{"file":".github/workflows/security.yml","host":"github-actions","line":10,"service":null}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"github-actions","ID":"github-actions/999-multibots-telegraf/security","KIND":"cron","NAME":"Security Checks","NOTE":"GitHub Actions on this repository have not run since 2026-06-02 (billing); the schedule stands in the file but does not fire.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 3 * * 0","SERVICE":".github/workflows/security.yml","SUMMARY_EN":"Runs the weekly security sweep: secret detection over the full history, dependency audit and code scanning.","SUMMARY_RU":"Еженедельная проверка безопасности: поиск секретов по всей истории, аудит зависимостей и сканирование кода.","TZ":"UTC"},"health":"ok","id":"github-actions/999-multibots-telegraf/security","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_security","runNow":{"kind":"link","url":"https://github.com/gHashTag/999-multibots-telegraf/actions/workflows/security.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"a52c7cb12f360055159d34a16d47dfa989757788833eb59f0d027fbb3e3637eb","specPath":"specs/crons/999-multibots-telegraf-security.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/t27/blob/e804cd062af9bd4424a8579ea54b1f21381580de/.github/workflows/formal-mutation.yml#L25","where":{"file":".github/workflows/formal-mutation.yml","host":"github-actions","line":25,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/t27/formal-mutation","KIND":"cron","NAME":"formal-mutation","NOTE":"Weekly rather than per-push because a full run costs about thirty yosys invocations.","ON_FAILURE":"log","REPO":"t27","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 5 * * 1","SERVICE":".github/workflows/formal-mutation.yml","SUMMARY_EN":"Proves the formal gates actually bite by mutating the generated RTL once a week and requiring each gate to go red for its own mutation.","SUMMARY_RU":"Раз в неделю мутирует сгенерированный RTL и требует, чтобы каждый формальный гейт покраснел на своей мутации — доказательство, что гейты кусаются.","TZ":"UTC"},"health":"ok","id":"github-actions/t27/formal-mutation","inSpecCorpus":false,"messages":[],"moduleName":"cron_t27_formal_mutation","runNow":{"kind":"link","url":"https://github.com/gHashTag/t27/actions/workflows/formal-mutation.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"dc60fffb89276f6f09374027ee7cf691a994d9f12eafd7347bee0bee7afd9f05","specPath":"specs/crons/t27-formal-mutation.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/t27/blob/e804cd062af9bd4424a8579ea54b1f21381580de/.github/workflows/pr-dashboard.yml#L6","where":{"file":".github/workflows/pr-dashboard.yml","host":"github-actions","line":6,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/t27/pr-dashboard","KIND":"cron","NAME":"PR Dashboard","ON_FAILURE":"log","REPO":"t27","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 * * * *","SERVICE":".github/workflows/pr-dashboard.yml","SUMMARY_EN":"Rebuilds the pull-request dashboard every hour from the current state of the open pull requests.","SUMMARY_RU":"Каждый час пересобирает панель pull request из текущего состояния открытых PR.","TZ":"UTC"},"health":"ok","id":"github-actions/t27/pr-dashboard","inSpecCorpus":false,"messages":[],"moduleName":"cron_t27_pr_dashboard","runNow":{"kind":"link","url":"https://github.com/gHashTag/t27/actions/workflows/pr-dashboard.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"be97c3da5f58a2c411a91d4b2126e0cc38122941d85de8bd553fb926e3f241e7","specPath":"specs/crons/t27-pr-dashboard.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/agent-cron-cleanup.yml#L4","where":{"file":".github/workflows/agent-cron-cleanup.yml","host":"github-actions","line":4,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/agent-cron-cleanup","KIND":"cron","NAME":"Trinity Agent Cron Cleanup","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 */2 * * *","SERVICE":".github/workflows/agent-cron-cleanup.yml","SUMMARY_EN":"Finds agent deployments that got stuck and kills them, every two hours.","SUMMARY_RU":"Каждые два часа находит застрявшие деплои агентов и убивает их.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/agent-cron-cleanup","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_agent_cron_cleanup","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/agent-cron-cleanup.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"85e0de20901a5406bc238a0e1acc75977f23710f0e940d84a687272fd4e22c2b","specPath":"specs/crons/trinity-agent-cron-cleanup.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/agent-queue-drain.yml#L4","where":{"file":".github/workflows/agent-queue-drain.yml","host":"github-actions","line":4,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/agent-queue-drain","KIND":"cron","NAME":"Trinity Agent Queue Drain","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"dispatches agent-spawn-pool.yml for a queued issue; no named skill invocation found in source","SCHEDULE":"*/5 * * * *","SERVICE":".github/workflows/agent-queue-drain.yml","SUMMARY_EN":"Looks for queued issues and free agent slots every five minutes and starts the next agent when there is room.","SUMMARY_RU":"Каждые пять минут ищет задачи в очереди и свободные слоты агентов и запускает следующего агента, когда есть место.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/agent-queue-drain","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_agent_queue_drain","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/agent-queue-drain.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"bcc0041f566bf39a56f3452ad6566a300bcd53ef1d852e64c15aefd2f62a166f","specPath":"specs/crons/trinity-agent-queue-drain.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/brain-ci.yml#L11","where":{"file":".github/workflows/brain-ci.yml","host":"github-actions","line":11,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/brain-ci","KIND":"cron","NAME":"S³AI Brain CI — \"Functional MRI\" Gate","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 0 * * 0","SERVICE":".github/workflows/brain-ci.yml","SUMMARY_EN":"Runs the weekly brain stress test, the long version of the health gate that every commit already gets.","SUMMARY_RU":"Еженедельный стресс-тест мозга — длинная версия гейта здоровья, который получает каждый коммит.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/brain-ci","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_brain_ci","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/brain-ci.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"e17474cf9b75078e3abd48f6bee556af33c2de745b315631b2bd2cc385a07fcb","specPath":"specs/crons/trinity-brain-ci.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/codegen.yml#L10","where":{"file":".github/workflows/codegen.yml","host":"github-actions","line":10,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/codegen","KIND":"cron","NAME":"Codegen Validation","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 0 * * *","SERVICE":".github/workflows/codegen.yml","SUMMARY_EN":"Validates the VIBEE code generator once a day, independently of whether anyone pushed.","SUMMARY_RU":"Раз в день проверяет генератор кода VIBEE независимо от того, пушил ли кто-нибудь.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/codegen","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_codegen","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/codegen.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"425e572321b5028df50a0e07c159abd99adfbd45d104c6b7d3f2143515e06005","specPath":"specs/crons/trinity-codegen.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/discover-callers.yml#L16","where":{"file":".github/workflows/discover-callers.yml","host":"github-actions","line":16,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/discover-callers","KIND":"cron","NAME":"Discover RTL check callers","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"41 5 * * *","SERVICE":".github/workflows/discover-callers.yml","SUMMARY_EN":"Reads GitHub for repositories that run the RTL check, so the gallery counts its own users instead of being hand-written.","SUMMARY_RU":"Читает GitHub в поисках репозиториев, запускающих RTL-проверку, чтобы галерея считала своих пользователей сама.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/discover-callers","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_discover_callers","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/discover-callers.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"fea4b5a5340b022cad97371938c449d14fe5470772b9a3f6854dc4d978685788","specPath":"specs/crons/trinity-discover-callers.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/pages-health-check.yml#L17","where":{"file":".github/workflows/pages-health-check.yml","host":"github-actions","line":17,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/pages-health-check","KIND":"cron","NAME":"Pages Health Check","NOTE":"An earlier version judged a bare status code, failed ninety-six times a day on a site that was up, and dispatched a redeploy each time.","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"*/15 * * * *","SERVICE":".github/workflows/pages-health-check.yml","SUMMARY_EN":"Fetches the published site every fifteen minutes, follows the redirect, and redeploys only when the evidence says the deploy is what broke.","SUMMARY_RU":"Каждые пятнадцать минут запрашивает опубликованный сайт, следует редиректу и передеплоит только когда свидетельства говорят, что сломан именно деплой.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/pages-health-check","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_pages_health_check","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/pages-health-check.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"f5ef651617b4c600010ed119a9f023088c186fb67ec2b489c3a0db43503c90d4","specPath":"specs/crons/trinity-pages-health-check.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/signal-health-self.yml#L17","where":{"file":".github/workflows/signal-health-self.yml","host":"github-actions","line":17,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/signal-health-self","KIND":"cron","NAME":"Signal health (self)","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"23 4 * * *","SERVICE":".github/workflows/signal-health-self.yml","SUMMARY_EN":"Applies the signal-health check to the repository that sells it and writes the numbers into the website data.","SUMMARY_RU":"Применяет проверку signal-health к репозиторию, который её продаёт, и записывает числа в данные сайта.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/signal-health-self","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_signal_health_self","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/signal-health-self.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"861f57bd0158a46fccf12aef8c06cd91ee70522769c27445eec1b58534ace57f","specPath":"specs/crons/trinity-signal-health-self.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"github-actions","repoPrivate":false,"sourceUrl":"https://github.com/gHashTag/trinity/blob/cdc1d68d697fca070d9ad0f2d36aa4074de140f4/.github/workflows/site-live-gate.yml#L33","where":{"file":".github/workflows/site-live-gate.yml","host":"github-actions","line":33,"service":null}},"control":"github-actions-dispatch","discarded":0,"fields":{"CONTROL":"github-actions-dispatch","ENABLED":true,"HOST":"github-actions","ID":"github-actions/trinity/site-live-gate","KIND":"cron","NAME":"Site live gate","ON_FAILURE":"log","REPO":"trinity","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"17 6 * * *","SERVICE":".github/workflows/site-live-gate.yml","SUMMARY_EN":"Compares the content-hashed entry bundle served at the apex against a build of main, so that 'deployed' means a reader can actually see it.","SUMMARY_RU":"Сравнивает бандл с контент-хешем на апексе со сборкой main, чтобы «задеплоено» означало, что читатель это видит.","TZ":"UTC"},"health":"ok","id":"github-actions/trinity/site-live-gate","inSpecCorpus":false,"messages":[],"moduleName":"cron_trinity_site_live_gate","runNow":{"kind":"link","url":"https://github.com/gHashTag/trinity/actions/workflows/site-live-gate.yml","via":"github-actions"},"runs":[],"runsResolved":[],"sha256":"5b533385acc3ddef9f44d99336a55a6f20a58eb52e9088432c5b04266ff140a5","specPath":"specs/crons/trinity-site-live-gate.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/training/checkStuckTrainings.ts#L53","where":{"file":"src/inngest_app/functions/training/checkStuckTrainings.ts","host":"railway:999-multibots-telegraf","line":53,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/check-stuck-trainings","KIND":"cron","NAME":"check-stuck-trainings","NOTE":"Records whose Replicate id is still a placeholder are skipped, because there is nothing on the provider to ask about.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"*/30 * * * *","SERVICE":"src/inngest_app/functions/training/checkStuckTrainings.ts","SUMMARY_EN":"Finds model trainings still marked pending or processing past the stuck threshold and reconciles them against Replicate.","SUMMARY_RU":"Находит обучения моделей, застрявшие в pending или processing дольше порога, и сверяет их с Replicate.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/check-stuck-trainings","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_check_stuck_trainings","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"282b312ce35c0dbd17281c5401dd2d24231da9698a4f29a62a53ded9663894d3","specPath":"specs/crons/999-multibots-telegraf-check-stuck-trainings.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/analytics/dailySalesAdvisor.ts#L56","where":{"file":"src/inngest_app/functions/analytics/dailySalesAdvisor.ts","host":"railway:999-multibots-telegraf","line":56,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/daily-sales-advisor","KIND":"cron","NAME":"daily-sales-advisor","NOTE":"One report per owner across all of their bots, assembled from the payments ledger rather than from a balance column.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 9 * * *","SERVICE":"src/inngest_app/functions/analytics/dailySalesAdvisor.ts","SUMMARY_EN":"Builds every bot owner a morning report of yesterday's revenue, the week's costs and which services their users actually bought.","SUMMARY_RU":"Собирает каждому владельцу бота утренний отчёт: вчерашняя выручка, расходы недели и что реально покупали пользователи.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/daily-sales-advisor","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_daily_sales_advisor","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"723599e1eeca524a5545ee1fd2cc676dfc2a1ea387de5cbfff9b6698d854e24b","specPath":"specs/crons/999-multibots-telegraf-daily-sales-advisor.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/monitoring/criticalErrorMonitor.ts#L243","where":{"file":"src/inngest_app/functions/monitoring/criticalErrorMonitor.ts","host":"railway:999-multibots-telegraf","line":243,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/health-check","KIND":"cron","NAME":"health-check","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"*/30 * * * *","SERVICE":"src/inngest_app/functions/monitoring/criticalErrorMonitor.ts","SUMMARY_EN":"Probes the main API and the Inngest service every half hour and reports what answered.","SUMMARY_RU":"Каждые полчаса опрашивает основной API и сервис Inngest и сообщает, кто ответил.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/health-check","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_health_check","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"789f4cb8552910d61c8067aa18ced212634fd19769c47c573f723553770fc77a","specPath":"specs/crons/999-multibots-telegraf-health-check.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/monitoring/logMonitor.ts#L360","where":{"file":"src/inngest_app/functions/monitoring/logMonitor.ts","host":"railway:999-multibots-telegraf","line":360,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/log-monitor","KIND":"cron","NAME":"log-monitor","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 10 * * *","SERVICE":"src/inngest_app/functions/monitoring/logMonitor.ts","SUMMARY_EN":"Reads the application logs once a day, has a model summarise the errors and warnings, and sends the digest to Telegram.","SUMMARY_RU":"Раз в день читает логи приложения, поручает модели свести ошибки и предупреждения и отправляет дайджест в Telegram.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/log-monitor","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_log_monitor","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"ba0e734ee28be983d51518830d0b31121077fce42d2ec18a5b76573fbe8bbd01","specPath":"specs/crons/999-multibots-telegraf-log-monitor.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/webhookHealthGuard.ts#L218","where":{"file":"src/inngest_app/functions/webhookHealthGuard.ts","host":"railway:999-multibots-telegraf","line":218,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/periodic-webhook-health-check","KIND":"cron","NAME":"periodic-webhook-health-check","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"0 * * * *","SERVICE":"src/inngest_app/functions/webhookHealthGuard.ts","SUMMARY_EN":"Tests both Telegram webhook URLs every hour and sends the admin a critical alarm when neither of them answers.","SUMMARY_RU":"Каждый час проверяет оба URL вебхука Telegram и шлёт админу критическую тревогу, когда не отвечает ни один.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/periodic-webhook-health-check","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_periodic_webhook_health_check","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"73396dccfeefed992a70062c80e01231503560e19e94a458ab61b1e20a54d73b","specPath":"specs/crons/999-multibots-telegraf-periodic-webhook-health-check.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"inngest","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/inngest_app/functions/analytics/skillDetector.ts#L32","where":{"file":"src/inngest_app/functions/analytics/skillDetector.ts","host":"railway:999-multibots-telegraf","line":32,"service":"inngest"}},"control":"inngest-dashboard","discarded":0,"fields":{"CONTROL":"inngest-dashboard","ENABLED":true,"HOST":"inngest","ID":"inngest/999-multibots-telegraf/skill-detector","KIND":"cron","NAME":"skill-detector","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"the \"skills\" it saves are bot prompt patterns, not Claude Code skills; no skill invocation found in source","SCHEDULE":"0 10 * * *","SERVICE":"src/inngest_app/functions/analytics/skillDetector.ts","SUMMARY_EN":"Mines successful generations for repeated model and prompt patterns once a day and saves the new ones as reusable skills.","SUMMARY_RU":"Раз в день ищет в успешных генерациях повторяющиеся паттерны моделей и промптов и сохраняет новые как переиспользуемые навыки бота.","TZ":"UTC"},"health":"ok","id":"inngest/999-multibots-telegraf/skill-detector","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_skill_detector","runNow":{"kind":"link","url":"https://inngestinngest-production-6a21.up.railway.app","via":"inngest"},"runs":[],"runsResolved":[],"sha256":"24b9790a1593c180071015e649ea8a76c285be2b0e36429a839bd8ae970c43d1","specPath":"specs/crons/999-multibots-telegraf-skill-detector.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","kind":"railway-cron","repoPrivate":true,"sourceUrl":null,"where":{"file":null,"host":"railway:jobsearch-cron","line":null,"service":"jobsearch-cron"}},"control":"railway-dashboard","discarded":0,"fields":{"CONTROL":"railway-dashboard","ENABLED":true,"HOST":"railway-cron","ID":"railway-cron/999/jobsearch-cron","KIND":"cron","NAME":"jobsearch-cron","NOTE":"The schedule is configured in the Railway dashboard for project 999 and is not readable from the checkout; the probe below is what this catalog can verify.","ON_FAILURE":"unknown","REPO":"railway","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SCHEDULE":"","SCHEDULE_NOTE":"configured in the Railway dashboard (project 999); not readable from the checkout","SERVICE":"jobsearch-cron","SUMMARY_EN":"The scheduled job-search service of the Railway project, reachable at cron.t27.ai.","SUMMARY_RU":"Сервис поиска вакансий по расписанию в проекте Railway, доступен на cron.t27.ai.","TZ":"UTC"},"health":"ok","id":"railway-cron/999/jobsearch-cron","inSpecCorpus":false,"messages":[],"moduleName":"cron_railway_jobsearch_cron","runNow":{"kind":"link","url":"https://railway.com/project/564d9ebd-7aa8-44fe-93ec-e0b03c87158d/service/3196472e-b885-43e0-b553-0644664e4496","via":"railway"},"runs":[],"runsResolved":[],"sha256":"b432340af00d32a662b6eed392b7cc831ab34cfac2d7c98498f0dad0ee5c2fe6","specPath":"specs/crons/railway-jobsearch-cron.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/scripts/agent-autopilot.ts#L1118","where":{"file":"apps/vibee-editor/render/scripts/agent-autopilot.ts","host":"railway:vibee-render","line":1118,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/agent-autopilot-L1118","INTERVAL_MS":1800000,"KIND":"cron","NAME":"agent-autopilot-L1118","NOTE":"Daemon mode only, under --loop or AUTOPILOT_LOOP=1; AUTOPILOT_INTERVAL_MS overrides the thirty-minute period, with a one-minute floor.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/scripts/agent-autopilot.ts:1118","SUMMARY_EN":"Runs one autopilot cycle -- the generation pass and then the channel tick -- and skips the tick entirely when the previous cycle is still running.","SUMMARY_RU":"Один цикл автопилота — проход генерации и затем тик канала; тик пропускается целиком, если предыдущий цикл ещё идёт.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/agent-autopilot-L1118","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_agent_autopilot_L1118","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"02eacb7c854e538c0c44706e97fd8707eba59d37b310911feab2db23015deac5","specPath":"specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/services/bot-owner-billing.ts#L474","where":{"file":"src/services/bot-owner-billing.ts","host":"railway:999-multibots-telegraf","line":474,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/bot-owner-billing-L474","INTERVAL_MS":86400000,"KIND":"cron","NAME":"bot-owner-billing-L474","NOTE":"The first run after a restart is skipped on purpose, and an owner whose payments cannot be read is left alone rather than accused.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/services/bot-owner-billing.ts:474","SUMMARY_EN":"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.","SUMMARY_RU":"Пересчитывает долг каждого владельца бота по журналу платежей, предупреждает на мягком, тревожном и критическом порогах и отключает бота, три дня остающегося критическим.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/bot-owner-billing-L474","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_bot_owner_billing_L474","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"754df783f81a94dd9f7926083d1ff4eb84f7a2045e14eb00a8a3f1f540078b9f","specPath":"specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/services/crmProactive.ts#L375","where":{"file":"src/services/crmProactive.ts","host":"railway:999-multibots-telegraf","line":375,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/crmProactive-L375","INTERVAL_MS":1800000,"KIND":"cron","NAME":"crmProactive-L375","NOTE":"The period is CRM_PROACTIVE_MINUTES from src/index.ts, thirty minutes by default; the first sweep waits two minutes after start.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/services/crmProactive.ts:375","SUMMARY_EN":"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.","SUMMARY_RU":"Один проактивный ход продавца для владельца: обходит очередь CRM и кладёт перед ним следующую карточку-черновик без просьбы.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/crmProactive-L375","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_crmProactive_L375","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"73f69417f9fc199f3e4c27813978d6a726a1744c27bccaf0a00a46a3c5c79aed","specPath":"specs/crons/999-multibots-telegraf-crmProactive-L375.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/generate-jobs.ts#L164","where":{"file":"apps/vibee-editor/render/generate-jobs.ts","host":"railway:vibee-render","line":164,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/generate-jobs-L164","INTERVAL_MS":300000,"KIND":"cron","NAME":"generate-jobs-L164","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/generate-jobs.ts:164","SUMMARY_EN":"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.","SUMMARY_RU":"Через час после завершения выметает из памяти законченные задания генерации, когда потерявший соединение клиент успел забрать результат.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/generate-jobs-L164","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_generate_jobs_L164","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"d372fa11da3e124ed276b4cd05682a06c13660d58643dba0d72768a864e7d696","specPath":"specs/crons/999-multibots-telegraf-generate-jobs-L164.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/handlers/notificationHandler.ts#L318","where":{"file":"src/handlers/notificationHandler.ts","host":"railway:999-multibots-telegraf","line":318,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/notificationHandler-L318","INTERVAL_MS":60000,"KIND":"cron","NAME":"notificationHandler-L318","NOTE":"Wrapped in an exclusive tick, so two bot processes cannot deliver the same notification twice.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/handlers/notificationHandler.ts:318","SUMMARY_EN":"Drains the notification queue and delivers the Telegram messages waiting in it.","SUMMARY_RU":"Разгружает очередь уведомлений и доставляет ждущие в ней сообщения Telegram.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/notificationHandler-L318","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_notificationHandler_L318","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"570287be7228b3d4ebc8760220a0c5ec44740fb40f6a393c53aed1b15f139a15","specPath":"specs/crons/999-multibots-telegraf-notificationHandler-L318.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/handlers/notificationHandler.ts#L324","where":{"file":"src/handlers/notificationHandler.ts","host":"railway:999-multibots-telegraf","line":324,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/notificationHandler-L324","INTERVAL_MS":3600000,"KIND":"cron","NAME":"notificationHandler-L324","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/handlers/notificationHandler.ts:324","SUMMARY_EN":"Deletes notification messages old enough that nobody is going to read them.","SUMMARY_RU":"Удаляет уведомления, настолько старые, что их уже никто не прочитает.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/notificationHandler-L324","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_notificationHandler_L324","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"df310b1b365c5222e3b3d501733bdd7fbe15798dfb9ea56af313fc3e57536646","specPath":"specs/crons/999-multibots-telegraf-notificationHandler-L324.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/utils/production-monitor.ts#L80","where":{"file":"src/utils/production-monitor.ts","host":"railway:999-multibots-telegraf","line":80,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":false,"HOST":"timer","ID":"timer/999-multibots-telegraf/production-monitor-L80","INTERVAL_MS":300000,"KIND":"cron","NAME":"production-monitor-L80","NOTE":"Started only by running this module from the command line with the `monitor` argument; the bot's own entry point never starts it.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/utils/production-monitor.ts:80","SUMMARY_EN":"Re-runs the container, nginx, port, domain, webhook and system-resource checks, and applies the fixes that are enabled.","SUMMARY_RU":"Повторяет проверки контейнера, nginx, порта, домена, вебхука и ресурсов системы и применяет включённые исправления.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/production-monitor-L80","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_production_monitor_L80","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"0278cd02baea58a1460f060a32e5cffdb25bbd3aaa086427e65f137983458832","specPath":"specs/crons/999-multibots-telegraf-production-monitor-L80.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/services/provider-health-monitor.ts#L344","where":{"file":"src/services/provider-health-monitor.ts","host":"railway:999-multibots-telegraf","line":344,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/provider-health-monitor-L344","INTERVAL_MS":300000,"KIND":"cron","NAME":"provider-health-monitor-L344","NOTE":"Providers listed in HEALTH_SKIP_PROVIDERS are deliberately not watched and do not raise the alarm.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/services/provider-health-monitor.ts:344","SUMMARY_EN":"Checks every AI provider that is not parked and records whether it is currently answering.","SUMMARY_RU":"Проверяет каждого не отключённого AI-провайдера и записывает, отвечает ли он сейчас.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/provider-health-monitor-L344","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_provider_health_monitor_L344","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"e8602b2c67a38984c1fa30a9841095d8f946930bcd1a28fbca145e89c6de41a1","specPath":"specs/crons/999-multibots-telegraf-provider-health-monitor-L344.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/render-server.ts#L10845","where":{"file":"apps/vibee-editor/render/render-server.ts","host":"railway:vibee-render","line":10845,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/render-server-L10845","INTERVAL_MS":30000,"KIND":"cron","NAME":"render-server-L10845","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/render-server.ts:10845","SUMMARY_EN":"Pings every websocket peer and drops the ones that did not answer the previous round.","SUMMARY_RU":"Пингует каждого websocket-пира и отключает тех, кто не ответил в прошлом раунде.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/render-server-L10845","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_render_server_L10845","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"4907bfb806f34985976153cd83f2ac4929435b542b9394e318239b6e8266c5f7","specPath":"specs/crons/999-multibots-telegraf-render-server-L10845.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/render-server.ts#L2301","where":{"file":"apps/vibee-editor/render/render-server.ts","host":"railway:vibee-render","line":2301,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/render-server-L2301","INTERVAL_MS":60000,"KIND":"cron","NAME":"render-server-L2301","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/render-server.ts:2301","SUMMARY_EN":"Removes render jobs older than an hour from the in-memory job map.","SUMMARY_RU":"Удаляет из карты в памяти задания рендера старше часа.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/render-server-L2301","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_render_server_L2301","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"624490022ceeae840f69c733bdd2def4eb77177a7c1a3b5aa51900ba2a0cfd00","specPath":"specs/crons/999-multibots-telegraf-render-server-L2301.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/render-server.ts#L2325","where":{"file":"apps/vibee-editor/render/render-server.ts","host":"railway:vibee-render","line":2325,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/render-server-L2325","INTERVAL_MS":600000,"KIND":"cron","NAME":"render-server-L2325","NOTE":"Checks often and writes rarely: the cursor lives in the database, because this Railway project has no volume and a deploy wipes local state.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/render-server.ts:2325","SUMMARY_EN":"Reads the hive journal and delivers the queen's report to the keepers, passing alarms straight through and batching ordinary events.","SUMMARY_RU":"Читает журнал улья и доставляет отчёт королевы хранителям: тревоги — сразу, обычные события — пакетом.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/render-server-L2325","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_render_server_L2325","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"a54d8116025cfc6948ccdb3d5f3e0a45bf8db10c6770e3cdb1ecaccc840ef75d","specPath":"specs/crons/999-multibots-telegraf-render-server-L2325.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/apps/vibee-editor/render/render-server.ts#L919","where":{"file":"apps/vibee-editor/render/render-server.ts","host":"railway:vibee-render","line":919,"service":"render"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/render-server-L919","INTERVAL_MS":5000,"KIND":"cron","NAME":"render-server-L919","NOTE":"Only starts when a session signing key is configured; a deployed render server refuses to boot without one.","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"apps/vibee-editor/render/render-server.ts:919","SUMMARY_EN":"Re-reads the session revocation list from Postgres so a revoked browser session stops being accepted.","SUMMARY_RU":"Перечитывает из Postgres список отозванных сессий, чтобы отозванная сессия браузера перестала приниматься.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/render-server-L919","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_render_server_L919","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"c720acbe4557f1edba004b69490ddf0e5af4e9ed533c30baa710eb4f1de10a6f","specPath":"specs/crons/999-multibots-telegraf-render-server-L919.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/modules/videoGenerator/taskCache.ts#L27","where":{"file":"src/modules/videoGenerator/taskCache.ts","host":"railway:999-multibots-telegraf","line":27,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/taskCache-L27","INTERVAL_MS":300000,"KIND":"cron","NAME":"taskCache-L27","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/modules/videoGenerator/taskCache.ts:27","SUMMARY_EN":"Drops video-generation cache entries past their fifteen-minute lifetime so a finished task cannot block the next request.","SUMMARY_RU":"Сбрасывает записи кэша видеогенерации старше пятнадцати минут, чтобы завершённая задача не блокировала следующий запрос.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/taskCache-L27","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_taskCache_L27","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"b9638cccffe2a4a015b4f2605aeb118a5199097ef41c462cba748a57db69c3ca","specPath":"specs/crons/999-multibots-telegraf-taskCache-L27.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","kind":"timer","repoPrivate":true,"sourceUrl":"https://github.com/gHashTag/999-multibots-telegraf/blob/3f01d449e5a04d37f6ff4d76d5572f499c201789/src/services/video-task-store.ts#L156","where":{"file":"src/services/video-task-store.ts","host":"railway:999-multibots-telegraf","line":156,"service":"bot"}},"control":"code-only","discarded":0,"fields":{"CONTROL":"code-only","ENABLED":true,"HOST":"timer","ID":"timer/999-multibots-telegraf/video-task-store-L156","INTERVAL_MS":600000,"KIND":"cron","NAME":"video-task-store-L156","ON_FAILURE":"log","REPO":"999-multibots-telegraf","RUNS":[],"RUNS_NOTE":"no skill invocation found in source","SERVICE":"src/services/video-task-store.ts:156","SUMMARY_EN":"Forgets video tasks older than an hour and rewrites the store to disk when anything was removed.","SUMMARY_RU":"Забывает видеозадачи старше часа и переписывает хранилище на диск, если что-то было удалено.","TZ":"UTC"},"health":"ok","id":"timer/999-multibots-telegraf/video-task-store-L156","inSpecCorpus":false,"messages":[],"moduleName":"cron_999_multibots_telegraf_video_task_store_L156","runNow":{"kind":"disabled","reason":"timer"},"runs":[],"runsResolved":[],"sha256":"7838f87cc430da75929c9a9e71b94361fa55ed87ba2adda3682efe1a9eebe8be","specPath":"specs/crons/999-multibots-telegraf-video-task-store-L156.t27","typecheckOk":true,"witness":"spec+code"}],"generatedAt":"2026-09-09T12:02:57.338Z","version":1} diff --git a/apps/website/public/skills/spec-skills.json b/apps/website/public/skills/spec-skills.json new file mode 100644 index 0000000000..4f7b511791 --- /dev/null +++ b/apps/website/public/skills/spec-skills.json @@ -0,0 +1 @@ +{"codeOnly":[],"compilerWasmSha256":"4d9c0447b5ca288790ab03d3dffc2dda629ed47af9d6731a105fc923aea0aee4","contentSha256":"58bc5107b885b48721c485de79904fed2ca2ab31b43716cf85209ec6e0f1c877","counts":{"codeOnly":0,"runBy":0,"specOnly":0,"specPlusCode":26,"specs":26,"typecheckOk":26},"generatedAt":"2026-09-09T12:02:57.338Z","skills":[{"code":{"health":"warn","link":"unbound","path":"t27/measure-corpus/SKILL.md","sha256":"b8fb7503f660a81b2fee57cde8998b18bbf6c6f334ca2ee69424c3ccf99f3211"},"discarded":0,"fields":{"COMMAND":"","ENABLED":true,"ID":"t27/measure-corpus","KIND":"skill","NAME":"MEASURE THE CORPUS","REPO":"t27","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Pick the right instrument for a claim about the t27 spec corpus, and avoid the specific traps that have produced wrong published numbers. Use before quoting any figure about specs, errors, or gates.","SUMMARY_RU":"Выбор правильного инструмента для любого утверждения о корпусе спек t27 и список ловушек, которые уже давали неверные опубликованные числа. Использовать перед тем, как цитировать любое число о корпусе.","TAGS":["corpus","measurement","honesty"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/measure-corpus","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_measure_corpus","runBy":[],"sha256":"3c31c8d1f0dabaf6a50ef17a8346a30c142a89705ace1bdb23ef603137eb6521","specPath":"specs/skills/t27-measure-corpus.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"t27/phi-loop/SKILL.md","sha256":"d8af7f6a9cbc39ed4557291528f36748e07292ef752f5159e2827f59b2dd7bc6"},"discarded":0,"fields":{"COMMAND":"","ENABLED":true,"ID":"t27/phi-loop","KIND":"skill","NAME":"PHI LOOP","REPO":"t27","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Execute 9-phase spec-first development workflow (Issue → Spec → TDD → Code → Gen → Seal → Verify → Land → Learn)","SUMMARY_RU":"Девятифазный цикл spec-first разработки: Issue → Spec → TDD → Code → Gen → Seal → Verify → Land → Learn.","TAGS":["workflow","spec-first","tdd"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/phi-loop","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_phi_loop","runBy":[],"sha256":"f36becd62e64118f10b498a691a6d05396f81f44f4c1d73f71350e8c0527fc4b","specPath":"specs/skills/t27-phi-loop.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","link":"bound","path":"t27/tri/skill.md","sha256":"8fb4d4796c2dc7adab9670a65709ce0451633e6f1f076d5e490270596636042a"},"discarded":0,"fields":{"COMMAND":"/tri","ENABLED":true,"ID":"t27/tri","KIND":"skill","NAME":"tri","REPO":"t27","SOURCE":"skill.md","SPECS":["compiler/skill/registry.t27"],"SUMMARY_EN":"This skill should be used when user asks to \"tri skill begin\", \"PHI LOOP\", \"edit .t27 spec\", \"seal hash\", \"tri gen\", \"tri test\", \"tri verdict\", \"tri experience save\", \"tri notebook query\", \"tri notebook wrapup\", or any task requiring spe...","SUMMARY_RU":"Команды tri для работы со скилом: begin, правка .t27-спеки, seal hash, gen, test, verdict, experience. Реестр скилов описан в compiler/skill/registry.t27.","TAGS":["tri","workflow","seal"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/tri","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_tri","runBy":[],"sha256":"988a8018ba55c41bd6736867ba5384f5f0ba106a8396f5bace2c6c0f58d43bf3","specPath":"specs/skills/t27-tri.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","link":"bound","path":"t27/tri-pipeline/SKILL.md","sha256":"b4bfc290b86a4a1d4513cf9ace0a43526ca8fe98e0d97ff1eb59ea7997aa7427"},"discarded":0,"fields":{"COMMAND":"","ENABLED":true,"ID":"t27/tri-pipeline","KIND":"skill","NAME":"TRI Pipeline","REPO":"t27","SOURCE":"SKILL.md","SPECS":["specs/pipeline/e2e_test.t27"],"SUMMARY_EN":"Execute tri commands (gen, test, verify, seal, verdict) for spec-first development","SUMMARY_RU":"Выполнение команд tri (gen, test, verify, seal, verdict) для spec-first разработки.","TAGS":["tri","pipeline","verify"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/tri-pipeline","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_tri_pipeline","runBy":[],"sha256":"9e590a267b529c60e84e039571318da1824f37b6f1b496ad25611b21922ce2cf","specPath":"specs/skills/t27-tri-pipeline.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"t27/wave-audit/SKILL.md","sha256":"4cd0dd22105ae7fe86091a69ec297df729661181f147c0b59f8e8e94a90e9100"},"discarded":0,"fields":{"COMMAND":"","ENABLED":true,"ID":"t27/wave-audit","KIND":"skill","NAME":"Wave Audit — Comprehensive Project Analysis","REPO":"t27","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Full-spectrum audit of t27 project: GitHub issues analysis, weakness identification, SOTA research review, decomposed plan, and implementation of critical fixes.","SUMMARY_RU":"Полный аудит проекта t27: разбор GitHub issues, поиск слабых мест, обзор SOTA, декомпозированный план и реализация критичных исправлений.","TAGS":["audit","issues","plan"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/wave-audit","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_wave_audit","runBy":[],"sha256":"69a84b2ad3a02d62e95cf1833edefc6d7aa05a78ae0d91631dc3548c68963d99","specPath":"specs/skills/t27-wave-audit.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"ok","link":"bound","path":"t27/wrap-up/skill.md","sha256":"a4a9b4fd9d022c212add3479cde725f6b9bd56bc31d2d600ba771cbd009329f9"},"discarded":0,"fields":{"COMMAND":"/wrap-up","ENABLED":true,"ID":"t27/wrap-up","KIND":"skill","NAME":"wrap-up","REPO":"t27","SOURCE":"skill.md","SPECS":["specs/automation/wrapup-auto.t27"],"SUMMARY_EN":"Format and upload session wrap-up to NotebookLM for persistent semantic memory","SUMMARY_RU":"Оформление и загрузка итогов сессии в NotebookLM как постоянной семантической памяти.","TAGS":["memory","notebooklm","session"],"TIMEOUT_MIN":30},"health":"ok","id":"t27/wrap-up","inSpecCorpus":false,"messages":[],"moduleName":"skill_t27_wrap_up","runBy":[],"sha256":"486fe9f0618cb04c0ac587433bb3d88f91f1e5c5083b4ead8f206366b3da31e6","specPath":"specs/skills/t27-wrap-up.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/blog-post/SKILL.md","sha256":"2cb755fd4a60a76d568a0742aeda34da60a2441774139646e0ebf4bd2a185493"},"discarded":0,"fields":{"COMMAND":"/blog-post","ENABLED":true,"ID":"trinity/blog-post","KIND":"skill","NAME":"blog-post","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Write and publish a post to the t27.ai blog. Covers where the blog actually lives (not where it looks like it lives), the Post schema with its required receipts and openQuestions, the honesty rules the data file itself imposes, and the b...","SUMMARY_RU":"Написать и опубликовать пост в блог t27.ai: где блог реально живёт, схема Post с обязательными свидетельствами, обложка и проверки.","TAGS":["blog","publish","website"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/blog-post","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_blog_post","runBy":[],"sha256":"d3065706baac0519f8a8de3d2f94258b0d1c2cd1b3cfb09082987057bd629c5f","specPath":"specs/skills/trinity-blog-post.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/board-sync/SKILL.md","sha256":"f4bfa63737cf7a2078ae6e42f68943384214159243243ae8ec971ddeaac1825e"},"discarded":0,"fields":{"COMMAND":"/board [sync|audit|fix]","ENABLED":true,"ID":"trinity/board-sync","KIND":"skill","NAME":"board","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"GitHub Project Board auto-sync — fill empty fields, sync labels with board status, audit field completeness. Keeps board as realtime source of truth.","SUMMARY_RU":"Автосинхронизация GitHub Project Board: заполнить пустые поля, согласовать метки со статусом доски, проверить полноту полей.","TAGS":["github","board","labels"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/board-sync","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_board_sync","runBy":[],"sha256":"1c2852874eba063aef74a871c06e5d39e3dfc1931501c901178ccc2b26750031","specPath":"specs/skills/trinity-board-sync.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/cloud/SKILL.md","sha256":"53aa338dddc4e6b8a0fd82f6d3b8dc9ac9b245b3457559c3c44f325ede0130e6"},"discarded":0,"fields":{"COMMAND":"/cloud [status|agents|events|full]","ENABLED":true,"ID":"trinity/cloud","KIND":"skill","NAME":"cloud","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Show Trinity Cloud Dev dashboard — containers, training farm, issues, PRs, costs. Use when checking agent swarm status, cloud containers, or CI/CD pipeline.","SUMMARY_RU":"Панель Trinity Cloud Dev: контейнеры, тренировочная ферма, issues, PR, расходы. Для проверки состояния роя агентов и CI/CD.","TAGS":["dashboard","railway","agents"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/cloud","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_cloud","runBy":[],"sha256":"cd4053e5fbcc8b258617f7046324cfbd7e36385d97777973c096d00d54480741","specPath":"specs/skills/trinity-cloud.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/doctor/SKILL.md","sha256":"68efdf79301066d5e64e72babc91ddb867899786186fa360e6775b8365059815"},"discarded":0,"fields":{"COMMAND":"/doctor [quick|full|scan|junk|docs|dupes] [lang:ru|en]","ENABLED":true,"ID":"trinity/doctor","KIND":"skill","NAME":"doctor","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"HEALER — diagnose, heal, commit dirty files, monitor junk & docs & duplicates, report honestly. Every loop = action + proof.","SUMMARY_RU":"ЛЕКАРЬ: диагностика, лечение, коммит грязных файлов, надзор за мусором, документацией и дубликатами, честный отчёт. Каждый цикл = действие + доказательство.","TAGS":["health","repo","report"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/doctor","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_doctor","runBy":[],"sha256":"3fa298e60cdf258547fa4ee5f3e706cbe181927e849b257a014389658a1fa51a","specPath":"specs/skills/trinity-doctor.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/farm-garden/SKILL.md","sha256":"6721489885755a64bfdd07f0407b0ddad366c713cb2723098c0fc63b4e196bb4"},"discarded":0,"fields":{"COMMAND":"","ENABLED":true,"ID":"trinity/farm-garden","KIND":"skill","NAME":"Garden — Quantum Gardener for Trinity Farm","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Autonomous farm population manager. Monitors, evolves, and recycles HSLM training workers.","SUMMARY_RU":"Автономный управляющий популяцией фермы: наблюдает, эволюционирует и перерабатывает рабочих обучения HSLM.","TAGS":["farm","hslm","workers"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/farm-garden","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_farm_garden","runBy":[],"sha256":"7abb994814dbd67a35ec42470efde0d29790ad7bc3488ca5f6c513e65f093f9f","specPath":"specs/skills/trinity-farm-garden.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/fpga-synth/SKILL.md","sha256":"de3915e66cdab0f3207f53f6ea22a85b258c8a998b614231eebf7afab2ca9ec3"},"discarded":0,"fields":{"COMMAND":"/fpga-synth ","ENABLED":true,"ID":"trinity/fpga-synth","KIND":"skill","NAME":"fpga-synth","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Run FPGA synthesis pipeline from .tri spec to bitstream. Use when working with Verilog, FPGA, or hardware synthesis.","SUMMARY_RU":"Конвейер синтеза FPGA от .tri-спеки до битстрима. Для работы с Verilog, FPGA и аппаратным синтезом.","TAGS":["fpga","verilog","synthesis"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/fpga-synth","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_fpga_synth","runBy":[],"sha256":"7e66e215f8023026012a944a0afa067ded090b9dafaaaa3a5a1d3efb3f49d851","specPath":"specs/skills/trinity-fpga-synth.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/god-mode/SKILL.md","sha256":"0c48e328ae4cf1cbaffef270e7dbe16fac59a7fba845547179bcfe078f65ee7a"},"discarded":0,"fields":{"COMMAND":"/god-mode [status|agents|tasks|violations]","ENABLED":true,"ID":"trinity/god-mode","KIND":"skill","NAME":"god-mode","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"GOD MODE — Agent monitoring dashboard. Shows swarm status, task queue, pipeline compliance, circuit breakers, git activity, and rule violations.","SUMMARY_RU":"GOD MODE: панель наблюдения за агентами — состояние роя, очередь задач, соблюдение конвейера, предохранители, активность git, нарушения правил.","TAGS":["dashboard","agents","monitoring"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/god-mode","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_god_mode","runBy":[],"sha256":"4c22f9162a8accaac18b143ef589ce815366f82a8ebb885b97e958da4cadf4ff","specPath":"specs/skills/trinity-god-mode.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/implement-issue/SKILL.md","sha256":"88ccafaece204489bd38ee3f2fbd5f7db3d38275c2ba7e5a9bfca2852be596bb"},"discarded":0,"fields":{"COMMAND":"/implement-issue [issue-number]","ENABLED":true,"ID":"trinity/implement-issue","KIND":"skill","NAME":"implement-issue","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Read a GitHub issue, create a branch, implement the solution, and open a PR. Use when given an issue number to implement.","SUMMARY_RU":"Прочитать GitHub issue, создать ветку, реализовать решение и открыть PR. Для работы по номеру задачи.","TAGS":["github","issue","pr"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/implement-issue","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_implement_issue","runBy":[],"sha256":"de794eab7c57b0c952c4a8796f17112fc502e0863483cde32484f5d2a28c79d9","specPath":"specs/skills/trinity-implement-issue.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/queen-hive-visuals/SKILL.md","sha256":"c6d914dc81c84fc13dacca37de258563261a451a139e3c1644bbdc8b7a97b414"},"discarded":0,"fields":{"COMMAND":"/queen-hive-visuals","ENABLED":true,"ID":"trinity/queen-hive-visuals","KIND":"skill","NAME":"queen-hive-visuals","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Maintain QUEEN's floating hive, original point-down TRINITY logo, sharp issue displays and matching camera interactions.","SUMMARY_RU":"Поддержка плавающего улья Королевы, оригинального логотипа TRINITY остриём вниз, чётких табло задач и согласованных движений камеры.","TAGS":["queen","ui","3d"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/queen-hive-visuals","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_queen_hive_visuals","runBy":[],"sha256":"294285559b23b65cb40bfd5f9725610b3b8864368b3941dbc9e679a18d1822d4","specPath":"specs/skills/trinity-queen-hive-visuals.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/review-code/SKILL.md","sha256":"e5712744615c2422d9a0c81413422e6e1a2ac97c4bfac8bb508f1cd04bf05d75"},"discarded":0,"fields":{"COMMAND":"/review-code [file-or-branch] (optional)","ENABLED":true,"ID":"trinity/review-code","KIND":"skill","NAME":"review-code","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Review recent code changes for bugs, style issues, and improvements. Use after writing code or before committing.","SUMMARY_RU":"Ревью недавних изменений кода: ошибки, стиль, улучшения. После написания кода или перед коммитом.","TAGS":["review","quality"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/review-code","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_review_code","runBy":[],"sha256":"0d75b743c985faa6fb32ac280b2fa6362c79b91eea69ee4e8407e3a2f103dba0","specPath":"specs/skills/trinity-review-code.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/run-tests/SKILL.md","sha256":"7ead75ab36b840ec5588013b0de8f2c23ed3a27a0d3a5160d7e68334e5992a79"},"discarded":0,"fields":{"COMMAND":"/run-tests [module] (optional - all, vsa, vm, bsd, hslm, tri-api)","ENABLED":true,"ID":"trinity/run-tests","KIND":"skill","NAME":"run-tests","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Build the project, check for warnings, run all tests, and report results. Quick health check for the codebase.","SUMMARY_RU":"Собрать проект, проверить предупреждения, прогнать все тесты и отчитаться. Быстрая проверка здоровья кодовой базы.","TAGS":["tests","build","zig"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/run-tests","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_run_tests","runBy":[],"sha256":"369ba6795545b5f5349fb0f42ebfeeaea7814c38d17631077ec24929d8d376f8","specPath":"specs/skills/trinity-run-tests.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/scholar/SKILL.md","sha256":"5c35431008a4d2694cf2e7ad65c01d6f1559d5836b8f7cfb0c3422f752d5b2c2"},"discarded":0,"fields":{"COMMAND":"/scholar [scan|eval|apply|full|report|topic:\"query\"]","ENABLED":true,"ID":"trinity/scholar","KIND":"skill","NAME":"scholar","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Self-Evolving Research Agent — scans web for relevant tech, evaluates findings, proposes improvements. Uses Perplexity Sonar API via MCP.","SUMMARY_RU":"Саморазвивающийся исследовательский агент: сканирует веб на релевантные технологии, оценивает находки, предлагает улучшения. Perplexity Sonar через MCP.","TAGS":["research","perplexity","mcp"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/scholar","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_scholar","runBy":[],"sha256":"604c99a48904afcaee2fe429500490ec7eb392f12c9ef5e902ae8d925a9a42e4","specPath":"specs/skills/trinity-scholar.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/status/SKILL.md","sha256":"e4bc75ff45cdc52968f1ef0ac6e1a1a482072f78e14283b1e71b6fe13a054890"},"discarded":0,"fields":{"COMMAND":"/status [quick|full] [lang:ru|en]","ENABLED":true,"ID":"trinity/status","KIND":"skill","NAME":"status","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Live system dashboard — 12-dimension ouroboros score, verdict v3, agents, build health, recommendations","SUMMARY_RU":"Живая панель системы: 12-мерная оценка уробороса, verdict v3, агенты, здоровье сборки, рекомендации.","TAGS":["dashboard","ouroboros","verdict"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/status","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_status","runBy":[],"sha256":"440ccc168123ea4e409e126670d6342a4907956e8aa1a86fa2cab1904a1e3cb9","specPath":"specs/skills/trinity-status.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/tech-tree/SKILL.md","sha256":"f84c8ea67ab696a9bc3828ccfe8b8708572fe5ddb7574f409ddf75c467744a0f"},"discarded":0,"fields":{"COMMAND":"/tree [next|path|blocked|update]","ENABLED":true,"ID":"trinity/tech-tree","KIND":"skill","NAME":"tree","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Technology dependency tree — shows issue DAG, prereqs, critical path, blocked/ready tasks. Single source of truth for work order.","SUMMARY_RU":"Дерево технологических зависимостей: DAG задач, предпосылки, критический путь, заблокированные и готовые задачи. Единый источник порядка работ.","TAGS":["issues","dag","planning"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/tech-tree","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_tech_tree","runBy":[],"sha256":"b6dc5a0924d1e1b52a73cf988b185a1a825fcc45de3665bed52fd87f11ba4952","specPath":"specs/skills/trinity-tech-tree.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/tri/SKILL.md","sha256":"4c38d1005219336c88583e985b5208653fa199d12481d98173604922dc35e4a5"},"discarded":0,"fields":{"COMMAND":"/tri [short] [full] [audit] [coverage] [lang:ru|en] [humor|prof|toxic|zen]","ENABLED":true,"ID":"trinity/tri","KIND":"skill","NAME":"tri","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"TRI status dashboard — agent thought mode by default, /tri full for visual dashboard","SUMMARY_RU":"Панель статуса TRI: по умолчанию режим мысли агента, /tri full — визуальная панель.","TAGS":["tri","dashboard","status"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/tri","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_tri","runBy":[],"sha256":"4dc1bc94bd5f932c468d1d8b901101d1adaf58db8c14fec1b8b7378226ebcb44","specPath":"specs/skills/trinity-tri.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/trinity-test/SKILL.md","sha256":"648c7f095f0b9739a17d7a5d47fbfbe3e6dc2b2bd940fb6cea837632940b6c56"},"discarded":0,"fields":{"COMMAND":"/trinity-test [module] (vsa, vm, firebird, all)","ENABLED":true,"ID":"trinity/trinity-test","KIND":"skill","NAME":"trinity-test","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Run Trinity test suites and analyze results. Use for testing VSA, VM, Firebird, WASM, or full test suite.","SUMMARY_RU":"Прогон тестовых наборов Trinity и разбор результатов: VSA, VM, Firebird, WASM или всё целиком.","TAGS":["tests","vsa","vm"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/trinity-test","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_trinity_test","runBy":[],"sha256":"1a4b9e4f6e6d2c842d5083131b720e7f59139751b79d5638d75238d89c1e54c5","specPath":"specs/skills/trinity-trinity-test.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/ux-wave/SKILL.md","sha256":"9690999e8d1e2091c80ef838e41a1d4dc48d9ffea1cf29a39294238f69954b28"},"discarded":0,"fields":{"COMMAND":"/ux-wave [wave-number] [audit]","ENABLED":true,"ID":"trinity/ux-wave","KIND":"skill","NAME":"ux-wave","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Autonomous UX improvement waves — launch 4 parallel agents per wave, audit every 5 waves, commit fixes. For Queen UI chat & network polish.","SUMMARY_RU":"Автономные волны улучшения UX: 4 параллельных агента на волну, аудит каждые 5 волн, коммит исправлений. Для чата и сети Queen UI.","TAGS":["ux","agents","queen"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/ux-wave","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_ux_wave","runBy":[],"sha256":"fe8db4b03fde346492d552a5d46072d6737f54e66215d5748d0f99c21d1ac44b","specPath":"specs/skills/trinity-ux-wave.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/vibee-gen/SKILL.md","sha256":"fc01e69f1395b590603692a29d9bb1ee533104bb716b5bb284738027df17b395"},"discarded":0,"fields":{"COMMAND":"/vibee-gen ","ENABLED":true,"ID":"trinity/vibee-gen","KIND":"skill","NAME":"vibee-gen","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Generate Zig or Verilog code from VIBEE specifications. Use when creating or updating .tri specs and regenerating code.","SUMMARY_RU":"Генерация кода Zig или Verilog из спецификаций VIBEE. При создании или обновлении .tri-спек и перегенерации кода.","TAGS":["codegen","zig","verilog"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/vibee-gen","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_vibee_gen","runBy":[],"sha256":"0a40c2f8a4d4a68222bfde35732d2745653384504108cccaa58deb629c0ba768","specPath":"specs/skills/trinity-vibee-gen.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/vsa-verify/SKILL.md","sha256":"8b9c4518b1bc5dd468b12697a436f7d959278466b5282e575709d7981722905b"},"discarded":0,"fields":{"COMMAND":"/vsa-verify [operation or proof to verify]","ENABLED":true,"ID":"trinity/vsa-verify","KIND":"skill","NAME":"vsa-verify","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Verify VSA (Vector Symbolic Architecture) mathematical proofs and operations. Use for math verification, bind/unbind/bundle testing, phi identity checks.","SUMMARY_RU":"Проверка математических доказательств и операций VSA: bind/unbind/bundle, тождества phi.","TAGS":["vsa","math","verify"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/vsa-verify","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_vsa_verify","runBy":[],"sha256":"189c8c44e4d46ce27103a3d4924487c835603443c818e4b3ef30d92397369010","specPath":"specs/skills/trinity-vsa-verify.t27","typecheckOk":true,"witness":"spec+code"},{"code":{"health":"warn","link":"unbound","path":"trinity/wave/SKILL.md","sha256":"ecf06fdfa467158f6758066aba41a8e49699f37e61c18e62716da765a7eb5487"},"discarded":0,"fields":{"COMMAND":"/wave [status|spawn |deploy|progress]","ENABLED":true,"ID":"trinity/wave","KIND":"skill","NAME":"wave","REPO":"trinity","SOURCE":"SKILL.md","SPECS":[],"SUMMARY_EN":"Farm wave management — create Railway accounts, spawn workers, deploy configs, track wave progress. Use for scaling training farm.","SUMMARY_RU":"Управление волнами фермы: аккаунты Railway, запуск рабочих, раскатка конфигов, прогресс волны. Для масштабирования тренировочной фермы.","TAGS":["farm","railway","scaling"],"TIMEOUT_MIN":30},"health":"ok","id":"trinity/wave","inSpecCorpus":false,"messages":[],"moduleName":"skill_trinity_wave","runBy":[],"sha256":"14207973cd3997cd997a1e480a0743071c5477fdd4a34443b15c226ccbb3c753","specPath":"specs/skills/trinity-wave.t27","typecheckOk":true,"witness":"spec+code"}],"version":1} diff --git a/apps/website/qa/agents-spec-contract.mjs b/apps/website/qa/agents-spec-contract.mjs new file mode 100644 index 0000000000..60ade93c0d --- /dev/null +++ b/apps/website/qa/agents-spec-contract.mjs @@ -0,0 +1,173 @@ +// The spec-first skills and crons catalogs, checked against what they claim. +// +// public/skills/spec-skills.json and public/crons/spec-crons.json are written +// by scripts/agents-from-specs.mjs from the .t27 files under +// public/t27/files/specs/{skills,crons}/ -- through the real compiler, not a +// regex. This gate asks the questions a generated file can still fail: is the +// committed JSON what the specs produce today (no stale output, no hand edit); +// does every entry point at a file whose bytes hash to what it says; does every +// witness label agree with the code catalog it names; does every RUNS link +// resolve both ways; and does the public page carry nothing shaped like a +// secret. It also pins the management-strip facts the page derives -- the +// canonical edit URL and the "run now" target per host -- so the buttons in the +// Explorer cannot silently point somewhere the spec does not. +// +// node --experimental-strip-types qa/agents-spec-contract.mjs + +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { existsSync, readFileSync } from 'node:fs' +import { join } from 'node:path' +import { generate, SKILLS_OUT, CRONS_OUT, HOSTS, CONTROLS, ON_FAILURE } from '../scripts/agents-from-specs.mjs' +import { canonicalSpecEditUrl, vendoredSpecUrl, specSlug } from '../src/lib/agentSpecs.ts' +import { MODULES } from '../src/lib/queenModules.ts' +import { HUD_VIEWS } from '../src/components/queenHud.ts' + +const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex') +const skills = JSON.parse(readFileSync(SKILLS_OUT, 'utf8')) +const crons = JSON.parse(readFileSync(CRONS_OUT, 'utf8')) +const skillsCode = JSON.parse(readFileSync('public/skills/manifest.json', 'utf8')) +const cronsCode = JSON.parse(readFileSync('public/crons/manifest.json', 'utf8')) +const t27Manifest = JSON.parse(readFileSync('public/t27/manifest.json', 'utf8')) +const corpusPaths = new Set(t27Manifest.specs.map((s) => s.path)) + +// 1. The committed output is what the specs produce today. +const fresh = await generate({ generatedAt: skills.generatedAt }) +assert.deepEqual(fresh.problems, [], 'the generator reports problems:\n' + fresh.problems.join('\n')) +assert.equal(skills.contentSha256, fresh.skills.contentSha256, `${SKILLS_OUT} is stale or hand-edited; run node scripts/agents-from-specs.mjs`) +assert.equal(crons.contentSha256, fresh.crons.contentSha256, `${CRONS_OUT} is stale or hand-edited; run node scripts/agents-from-specs.mjs`) +assert.equal(skills.compilerWasmSha256, sha256(readFileSync('public/t27/t27_compiler.wasm')), 'the catalog names a compiler other than the vendored one') +assert.equal(crons.compilerWasmSha256, skills.compilerWasmSha256) + +// 2. Nothing shaped like a secret on a public page. +const FORBIDDEN = [ + { name: 'telegram id', re: /\b\d{9,10}\b/ }, + { name: 'IPv4 address', re: /\b(?:\d{1,3}\.){3}\d{1,3}\b/ }, + { name: 'bearer token', re: /Bearer\s+[A-Za-z0-9._-]{10,}/ }, + { name: 'bot token', re: /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/ }, + { name: 'url credential', re: /:\/\/[^\s/:@]+:[^\s@]+@/ }, + { name: 'github token', re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/ }, +] +for (const [name, text] of [['spec-skills', JSON.stringify(skills)], ['spec-crons', JSON.stringify(crons)]]) { + for (const f of FORBIDDEN) { + const hit = f.re.exec(text) + assert.ok(!hit, `${name} carries something shaped like a ${f.name} (${hit ? hit[0].slice(0, 4) : ''}…)`) + } +} + +// 3. Every entry: a real file, the right bytes, an honest witness. +const WITNESS = new Set(['spec+code', 'spec-only']) +const HEALTH = new Set(['ok', 'warn', 'fail']) +const skillIds = new Set(skills.skills.map((s) => s.id)) +const cronIds = new Set(crons.crons.map((c) => c.id)) +assert.equal(skillIds.size, skills.skills.length, 'duplicate skill ids') +assert.equal(cronIds.size, crons.crons.length, 'duplicate cron ids') +const codeSkillIds = new Set(skillsCode.skills.map((s) => s.id)) +const codeCronIds = new Set(cronsCode.crons.map((c) => c.id)) + +for (const s of skills.skills) { + const file = join('public/t27/files', s.specPath) + assert.ok(existsSync(file), `${s.id}: ${s.specPath} is not in the vendored corpus`) + assert.equal(sha256(readFileSync(file)), s.sha256, `${s.id}: the spec bytes changed under the catalog`) + assert.equal(s.typecheckOk, true) + assert.equal(s.discarded, 0) + assert.ok(WITNESS.has(s.witness)) + assert.equal(s.witness, codeSkillIds.has(s.id) ? 'spec+code' : 'spec-only', `${s.id}: witness disagrees with the code catalog`) + assert.ok(HEALTH.has(s.health)) + if (s.witness === 'spec-only') assert.notEqual(s.health, 'ok', `${s.id}: spec-only must not read ok`) + assert.equal(s.inSpecCorpus, corpusPaths.has(s.specPath), `${s.id}: inSpecCorpus disagrees with public/t27/manifest.json`) + assert.equal(s.fields.ID, s.id) + assert.equal(s.fields.KIND, 'skill') + assert.ok(s.fields.SUMMARY_RU.length > 10 && s.fields.SUMMARY_EN.length > 10, `${s.id}: a summary is missing`) + assert.ok(/[а-яА-ЯёЁ]/.test(s.fields.SUMMARY_RU), `${s.id}: SUMMARY_RU is not Russian`) + for (const id of s.runBy) assert.ok(cronIds.has(id), `${s.id}: runBy names ${id}, which has no cron spec`) + for (const id of s.runBy) { + const cron = crons.crons.find((c) => c.id === id) + assert.ok(cron.runs.includes(s.id), `${s.id}: runBy ${id} but that cron's RUNS does not name it`) + } + // Management strip facts. + assert.equal(canonicalSpecEditUrl(s.specPath), `https://github.com/gHashTag/t27/edit/master/${s.specPath}`) + assert.equal(vendoredSpecUrl(s.specPath), `https://github.com/gHashTag/trinity/blob/main/apps/website/public/t27/files/${s.specPath}`) + assert.equal(specSlug(s.specPath), s.specPath.replace(/^specs\/skills\//, '').replace(/\.t27$/, '')) +} +for (const id of skills.codeOnly) assert.ok(codeSkillIds.has(id) && !skillIds.has(id), `codeOnly ${id} is wrong`) +for (const id of codeSkillIds) assert.ok(skillIds.has(id) || skills.codeOnly.includes(id), `${id} is in code but neither spec nor codeOnly`) + +for (const c of crons.crons) { + const file = join('public/t27/files', c.specPath) + assert.ok(existsSync(file), `${c.id}: ${c.specPath} is not in the vendored corpus`) + assert.equal(sha256(readFileSync(file)), c.sha256, `${c.id}: the spec bytes changed under the catalog`) + assert.equal(c.typecheckOk, true) + assert.equal(c.discarded, 0) + assert.ok(WITNESS.has(c.witness)) + assert.equal(c.witness, codeCronIds.has(c.id) ? 'spec+code' : 'spec-only', `${c.id}: witness disagrees with the code catalog`) + assert.ok(HEALTH.has(c.health)) + assert.equal(c.fields.ID, c.id) + assert.equal(c.fields.KIND, 'cron') + assert.ok(HOSTS.includes(c.fields.HOST)) + assert.ok(CONTROLS.includes(c.control)) + assert.equal(c.control, c.fields.CONTROL) + assert.ok(ON_FAILURE.includes(c.fields.ON_FAILURE)) + assert.ok(/[а-яА-ЯёЁ]/.test(c.fields.SUMMARY_RU), `${c.id}: SUMMARY_RU is not Russian`) + assert.deepEqual(c.runs, c.fields.RUNS) + assert.equal(c.runsResolved.length, c.runs.length) + for (const r of c.runsResolved) { + assert.equal(r.ok, skillIds.has(r.id), `${c.id}: RUNS ${r.id} resolution is wrong`) + if (!r.ok) assert.equal(c.health, 'fail', `${c.id}: an unresolved RUNS must read fail`) + if (r.ok) assert.ok(skills.skills.find((s) => s.id === r.id).runBy.includes(c.id), `${c.id}: RUNS ${r.id} has no reverse runBy`) + } + if (c.fields.HOST === 'timer') { + assert.ok(Number.isInteger(c.fields.INTERVAL_MS) && c.fields.INTERVAL_MS > 0) + assert.equal(c.fields.SCHEDULE, undefined) + assert.equal(c.control, 'code-only', `${c.id}: a timer cannot be controlled from outside its process`) + } else { + assert.equal(typeof c.fields.SCHEDULE, 'string') + if (c.fields.SCHEDULE === '') assert.ok(c.fields.SCHEDULE_NOTE, `${c.id}: an empty SCHEDULE needs a note`) + } + // A cron that runs no skill must say so in its own words, not stay silent. + if (c.runs.length === 0) assert.ok(c.fields.RUNS_NOTE.length > 0, `${c.id}: empty RUNS without RUNS_NOTE`) + // Management strip facts: where "run now" goes, per host, never invented. + const target = c.runNow + if (c.fields.HOST === 'github-actions') { + assert.equal(target.kind, 'link') + assert.equal(target.url, `https://github.com/gHashTag/${c.fields.REPO}/actions/workflows/${c.fields.SERVICE.split('/').pop()}`) + } else if (c.fields.HOST === 'railway-cron') { + assert.equal(target.kind, 'link') + assert.match(target.url, /^https:\/\/railway\.com\/project\/[0-9a-f-]{36}\/service\/[0-9a-f-]{36}$/) + } else if (c.fields.HOST === 'inngest') { + assert.equal(target.kind, 'link') + assert.equal(target.url, 'https://inngestinngest-production-6a21.up.railway.app') + } else { + assert.equal(target.kind, 'disabled') + } + assert.equal(canonicalSpecEditUrl(c.specPath), `https://github.com/gHashTag/t27/edit/master/${c.specPath}`) +} +for (const id of crons.codeOnly) assert.ok(codeCronIds.has(id) && !cronIds.has(id), `codeOnly ${id} is wrong`) +for (const id of codeCronIds) assert.ok(cronIds.has(id) || crons.codeOnly.includes(id), `${id} is in code but neither spec nor codeOnly`) + +// 4. The counts on the page are sums of the list. +assert.equal(skills.counts.specs, skills.skills.length) +assert.equal(skills.counts.specPlusCode, skills.skills.filter((s) => s.witness === 'spec+code').length) +assert.equal(skills.counts.codeOnly, skills.codeOnly.length) +assert.equal(skills.counts.runBy, skills.skills.filter((s) => s.runBy.length).length) +assert.equal(crons.counts.specs, crons.crons.length) +assert.equal(crons.counts.withRuns, crons.crons.filter((c) => c.runs.length).length) +for (const h of HOSTS) assert.equal(crons.counts.byHost[h], crons.crons.filter((c) => c.fields.HOST === h).length) + +// 5. The Queen knows both views, at the keys the modules list promises. +for (const tab of ['skills', 'crons']) { + const m = MODULES.find((x) => x.tab === tab) + assert.ok(m, `queenModules has no ${tab} entry`) + assert.ok(HUD_VIEWS.includes(tab), `HUD_VIEWS does not include ${tab}`) + assert.equal(String(HUD_VIEWS.indexOf(tab) + 1), m.key, `${tab}: the key on the card (${m.key}) is not the keyboard digit`) + for (const lang of ['en', 'ru']) assert.ok(m[lang].name && m[lang].hint && m[lang].body.length > 40, `${tab}: ${lang} copy missing`) +} +assert.equal(MODULES.length, HUD_VIEWS.length, 'every module is a view and every view a module') +for (const [i, m] of MODULES.entries()) assert.equal(m.key, String(i + 1), `module ${m.tab} carries key ${m.key} at position ${i + 1}`) + +console.log( + `agents-spec-contract: skills ${skills.skills.length} (spec+code ${skills.counts.specPlusCode}, spec-only ${skills.counts.specOnly}, code-only ${skills.counts.codeOnly}, run by a cron ${skills.counts.runBy}); ` + + `crons ${crons.crons.length} (spec+code ${crons.counts.specPlusCode}, spec-only ${crons.counts.specOnly}, code-only ${crons.counts.codeOnly}, with RUNS ${crons.counts.withRuns}); ` + + `in vendored corpus manifest: ${skills.skills.filter((s) => s.inSpecCorpus).length + crons.crons.filter((c) => c.inSpecCorpus).length}/${skills.skills.length + crons.crons.length}; ` + + `Queen views ${HUD_VIEWS.length}, modules ${MODULES.length}`, +) diff --git a/apps/website/scripts/agents-from-specs.mjs b/apps/website/scripts/agents-from-specs.mjs new file mode 100644 index 0000000000..bbbfc1b97d --- /dev/null +++ b/apps/website/scripts/agents-from-specs.mjs @@ -0,0 +1,440 @@ +#!/usr/bin/env node +// Skills and crons, generated FROM their .t27 specs by the real compiler. +// +// The spec is the source of truth: public/t27/files/specs/skills/*.t27 and +// public/t27/files/specs/crons/*.t27 are read here, analysed with the vendored +// t27_compiler.wasm (the same bytes the browser runs), and the constants are +// taken from the AST it returns. Nothing in a .t27 file is parsed with a regex. +// The code catalogs (public/skills/manifest.json, public/crons/manifest.json) +// are the WITNESS side: a spec whose ID they also list is `spec+code`; a spec +// they do not list is `spec-only` (warn); a catalog entry with no spec is +// `code-only` and is listed, not invented. +// +// node scripts/agents-from-specs.mjs write both JSON files +// node scripts/agents-from-specs.mjs --check regenerate in memory and diff +// +// Outputs: public/skills/spec-skills.json, public/crons/spec-crons.json. +// +// Compiler quirks this file depends on (verified against the vendored wasm): +// * a string literal comes back as ExprLiteral{value:'"..."'} -- the value +// keeps its quotes, so it is JSON-parsed, not trimmed; +// * an array literal comes back as ExprIdentifier{name:'["a","b"]'} -- the +// whole literal in the identifier's name, again JSON-parseable; +// * the JSON the wasm returns carries every non-ASCII byte as a separate +// char code < 256 (UTF-8 bytes read as Latin-1). `decodeBytes` undoes that +// only when every char code fits a byte, so already-correct text is left +// alone. Round-trip is exact for Cyrillic and the ✓ ⟲ ◷ glyphs; +// * `typecheck.ok` from this wasm is NECESSARY, NOT SUFFICIENT: it stays true +// for `pub const X : str = 5;`, for `[2]str = ["a"]` and for a u16 of +// 70000 (measured, see the test file). So the gate that actually holds is +// `checkSchema` below -- every constant's annotation, value shape, array +// length and integer range are checked here, from the AST, and a spec is +// only "ok" when both the compiler and the schema accept it. + +import { createHash } from 'node:crypto' +import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +export const SITE = resolve(dirname(fileURLToPath(import.meta.url)), '..') +export const SKILL_SPEC_DIR = 'specs/skills' +export const CRON_SPEC_DIR = 'specs/crons' +const CORPUS = 'public/t27/files' +const WASM = 'public/t27/t27_compiler.wasm' +export const SKILLS_OUT = 'public/skills/spec-skills.json' +export const CRONS_OUT = 'public/crons/spec-crons.json' +export const VERSION = 1 + +export const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex') + +// --------------------------------------------------------------------------- +// The compiler. +// --------------------------------------------------------------------------- +export async function loadCompiler(wasmBytes) { + const { instance } = await WebAssembly.instantiate(wasmBytes, {}) + const w = instance.exports + return function analyze(source) { + const bytes = new TextEncoder().encode(source) + const inPtr = w.t27_alloc(bytes.length) + new Uint8Array(w.memory.buffer, inPtr, bytes.length).set(bytes) + const outPtr = w.t27_analyze(inPtr, bytes.length) + const len = new DataView(w.memory.buffer).getUint32(outPtr, true) + const json = new TextDecoder().decode(new Uint8Array(w.memory.buffer, outPtr + 4, len)) + w.t27_free(outPtr, 4 + len) + return JSON.parse(json) + } +} + +export function decodeBytes(text) { + if (typeof text !== 'string') return text + let allBytes = true + for (const ch of text) { + if (ch.charCodeAt(0) > 255) { allBytes = false; break } + } + if (!allBytes) return text + try { + return new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(text, (c) => c.charCodeAt(0))) + } catch { + return text + } +} + +/** Every `pub const` of the module, from the AST, as `{ name: { type, value } }`. */ +export function constsOf(analysis) { + const out = {} + const decls = (analysis.ast?.children ?? []).filter((n) => n.kind === 'ConstDecl') + for (const d of decls) { + const expr = d.children?.[0] + if (!expr) continue + let value + if (expr.kind === 'ExprLiteral') { + const raw = decodeBytes(expr.value) + if (raw === 'true') value = true + else if (raw === 'false') value = false + else if (/^-?\d+$/.test(raw)) value = Number(raw) + else value = JSON.parse(raw) + } else if (expr.kind === 'ExprIdentifier') { + value = JSON.parse(decodeBytes(expr.name)) + } else { + throw new Error(`${d.name}: unsupported expression kind ${expr.kind}`) + } + if (d.name in out) throw new Error(`duplicate constant ${d.name}`) + out[d.name] = { type: d.type, value, pub: d.pub === true } + } + return out +} + +export function verdictOf(analysis) { + const discarded = (analysis.discarded?.length ?? 0) + (analysis.lexerDiscarded?.length ?? 0) + (analysis.swallowed?.length ?? 0) + return { + typecheckOk: analysis.typecheck?.ok === true, + errors: analysis.typecheck?.errorCount ?? (analysis.typecheck?.errors?.length ?? 0), + discarded, + hirOk: analysis.hir?.ok !== false, + } +} + +// --------------------------------------------------------------------------- +// Schema. +// --------------------------------------------------------------------------- +// Shapes: 'str' | 'bool' | 'arr' (of str) | 'u16' | 'u32'. +const SKILL_REQUIRED = { + KIND: 'str', ID: 'str', NAME: 'str', REPO: 'str', SOURCE: 'str', SUMMARY_EN: 'str', SUMMARY_RU: 'str', + COMMAND: 'str', SPECS: 'arr', TAGS: 'arr', ENABLED: 'bool', TIMEOUT_MIN: 'u16', +} +const CRON_REQUIRED = { + KIND: 'str', ID: 'str', NAME: 'str', HOST: 'str', REPO: 'str', SERVICE: 'str', SUMMARY_EN: 'str', SUMMARY_RU: 'str', + TZ: 'str', RUNS: 'arr', RUNS_NOTE: 'str', ENABLED: 'bool', ON_FAILURE: 'str', CONTROL: 'str', +} +const CRON_OPTIONAL = { SCHEDULE: 'str', SCHEDULE_NOTE: 'str', INTERVAL_MS: 'u32', NOTE: 'str' } +const INT_MAX = { u16: 0xffff, u32: 0xffffffff } +export const HOSTS = ['github-actions', 'inngest', 'railway-cron', 'timer'] +export const CONTROLS = ['github-actions-dispatch', 'railway-dashboard', 'inngest-dashboard', 'code-only'] +export const ON_FAILURE = ['issue', 'log', 'unknown'] + +/** Inngest's own dashboard on Railway; the only outside handle an Inngest function has. */ +export const INNGEST_DASHBOARD_URL = 'https://inngestinngest-production-6a21.up.railway.app' + +/** + * Where "run now" goes for a job, per host. Resolved at build time so the page + * never has to know Railway's ids: they live in scripts/railway-services.json, + * beside the service list the crons manifest is already built from. A timer + * inside a process has no outside handle, and the strip says so. + */ +export function runNowTarget(fields, railway) { + switch (fields.HOST) { + case 'github-actions': { + const file = fields.SERVICE.split('/').pop() ?? fields.SERVICE + return { kind: 'link', via: 'github-actions', url: `https://github.com/gHashTag/${fields.REPO}/actions/workflows/${file}` } + } + case 'railway-cron': { + const svc = (railway?.services ?? []).find((s) => s.name === fields.SERVICE) + if (!railway?.projectId || !svc?.serviceId) return { kind: 'disabled', reason: 'unknown-service' } + return { kind: 'link', via: 'railway', url: `https://railway.com/project/${railway.projectId}/service/${svc.serviceId}` } + } + case 'inngest': + return { kind: 'link', via: 'inngest', url: INNGEST_DASHBOARD_URL } + default: + return { kind: 'disabled', reason: 'timer' } + } +} + +/** Annotation + value agree with the shape; null when they do, a reason when they do not. */ +export function shapeProblem(decl, shape) { + const { type, value } = decl + switch (shape) { + case 'str': + if (type !== 'str') return `annotated ${type}, must be str` + if (typeof value !== 'string') return 'value is not a string literal' + return null + case 'bool': + if (type !== 'bool') return `annotated ${type}, must be bool` + if (typeof value !== 'boolean') return 'value is not true/false' + return null + case 'u16': + case 'u32': + if (type !== shape) return `annotated ${type}, must be ${shape}` + if (!Number.isInteger(value) || value < 0 || value > INT_MAX[shape]) return `value ${value} is not in ${shape} range` + return null + case 'arr': { + if (!Array.isArray(value) || !value.every((x) => typeof x === 'string')) return 'value is not an array of strings' + if (type !== `[${value.length}]str`) return `annotated ${type}, holds ${value.length} string(s)` + return null + } + default: + return `unknown shape ${shape}` + } +} + +export function checkSchema(consts, required, optional, file) { + const problems = [] + for (const [name, shape] of Object.entries(required)) { + if (!(name in consts)) { problems.push(`${file}: missing ${name}`); continue } + const bad = shapeProblem(consts[name], shape) + if (bad) problems.push(`${file}: ${name}: ${bad}`) + if (!consts[name].pub) problems.push(`${file}: ${name} must be pub`) + } + for (const [name, shape] of Object.entries(optional)) { + if (!(name in consts)) continue + const bad = shapeProblem(consts[name], shape) + if (bad) problems.push(`${file}: ${name}: ${bad}`) + if (!consts[name].pub) problems.push(`${file}: ${name} must be pub`) + } + for (const name of Object.keys(consts)) { + if (!(name in required) && !(name in optional)) problems.push(`${file}: unknown constant ${name}`) + } + return problems +} + +const plain = (consts) => Object.fromEntries(Object.entries(consts).map(([k, v]) => [k, v.value])) + +// --------------------------------------------------------------------------- +// Build. `files` = [{ path (corpus-relative), text }], `code` = the two manifests. +// --------------------------------------------------------------------------- +export function analyzeSpecFiles(analyze, files) { + return files.map((f) => { + const analysis = analyze(f.text) + return { path: f.path, sha256: sha256(Buffer.from(f.text, 'utf8')), consts: constsOf(analysis), verdict: verdictOf(analysis), moduleName: analysis.ast?.name ?? null } + }) +} + +export function buildSpecCatalogs({ skillSpecs, cronSpecs, skillsManifest, cronsManifest, t27Manifest, railway, compilerWasmSha256, generatedAt }) { + const problems = [] + const corpusPaths = new Set((t27Manifest?.specs ?? []).map((s) => s.path)) + const codeSkills = new Map((skillsManifest?.skills ?? []).map((s) => [s.id, s])) + const codeCrons = new Map((cronsManifest?.crons ?? []).map((c) => [c.id, c])) + + const skills = [] + const seenSkill = new Map() + for (const s of skillSpecs) { + const file = s.path + if (!s.verdict.typecheckOk || s.verdict.discarded > 0 || !s.verdict.hirOk) problems.push(`${file}: compiler verdict not clean (${JSON.stringify(s.verdict)})`) + problems.push(...checkSchema(s.consts, SKILL_REQUIRED, {}, file)) + const f = plain(s.consts) + if (f.KIND !== 'skill') problems.push(`${file}: KIND must be "skill"`) + if (typeof f.ID === 'string') { + if (seenSkill.has(f.ID)) problems.push(`${file}: duplicate skill ID ${f.ID} (also ${seenSkill.get(f.ID)})`) + seenSkill.set(f.ID, file) + } + const expectModule = `skill_${file.replace(/^specs\/skills\//, '').replace(/\.t27$/, '').replace(/[^A-Za-z0-9]+/g, '_')}` + if (s.moduleName && s.moduleName !== expectModule) problems.push(`${file}: module must be ${expectModule}, is ${s.moduleName}`) + const code = codeSkills.get(f.ID) + const specsMissing = (Array.isArray(f.SPECS) ? f.SPECS : []).filter((p) => !corpusPaths.has(p)) + skills.push({ + id: f.ID, + specPath: file, + sha256: s.sha256, + typecheckOk: s.verdict.typecheckOk, + discarded: s.verdict.discarded, + moduleName: s.moduleName, + inSpecCorpus: corpusPaths.has(file), + fields: f, + code: code ? { path: code.path, sha256: code.sha256, health: code.health, link: code.link } : null, + witness: code ? 'spec+code' : 'spec-only', + runBy: [], + health: code ? (specsMissing.length ? 'warn' : 'ok') : 'warn', + messages: [ + ...(code ? [] : [`no skill ${f.ID} in public/skills/manifest.json`]), + ...specsMissing.map((p) => `SPECS names ${p}, which is not in the vendored corpus manifest`), + ], + }) + } + + const crons = [] + const seenCron = new Map() + for (const c of cronSpecs) { + const file = c.path + if (!c.verdict.typecheckOk || c.verdict.discarded > 0 || !c.verdict.hirOk) problems.push(`${file}: compiler verdict not clean (${JSON.stringify(c.verdict)})`) + problems.push(...checkSchema(c.consts, CRON_REQUIRED, CRON_OPTIONAL, file)) + const f = plain(c.consts) + if (f.KIND !== 'cron') problems.push(`${file}: KIND must be "cron"`) + if (typeof f.ID === 'string') { + if (seenCron.has(f.ID)) problems.push(`${file}: duplicate cron ID ${f.ID} (also ${seenCron.get(f.ID)})`) + seenCron.set(f.ID, file) + } + const expectModule = `cron_${file.replace(/^specs\/crons\//, '').replace(/\.t27$/, '').replace(/[^A-Za-z0-9]+/g, '_')}` + if (c.moduleName && c.moduleName !== expectModule) problems.push(`${file}: module must be ${expectModule}, is ${c.moduleName}`) + if (f.HOST && !HOSTS.includes(f.HOST)) problems.push(`${file}: HOST ${f.HOST} is not one of ${HOSTS.join('|')}`) + if (f.CONTROL && !CONTROLS.includes(f.CONTROL)) problems.push(`${file}: CONTROL ${f.CONTROL} is not one of ${CONTROLS.join('|')}`) + if (f.ON_FAILURE && !ON_FAILURE.includes(f.ON_FAILURE)) problems.push(`${file}: ON_FAILURE ${f.ON_FAILURE} is not one of ${ON_FAILURE.join('|')}`) + const isTimer = f.HOST === 'timer' + if (isTimer && !('INTERVAL_MS' in f)) problems.push(`${file}: a timer needs INTERVAL_MS`) + if (isTimer && 'SCHEDULE' in f) problems.push(`${file}: a timer has INTERVAL_MS, not SCHEDULE`) + if (!isTimer && !('SCHEDULE' in f)) problems.push(`${file}: a scheduled job needs SCHEDULE`) + if (!isTimer && 'INTERVAL_MS' in f) problems.push(`${file}: only a timer has INTERVAL_MS`) + if (f.SCHEDULE === '' && !f.SCHEDULE_NOTE) problems.push(`${file}: an empty SCHEDULE needs SCHEDULE_NOTE`) + const code = codeCrons.get(f.ID) + const runs = Array.isArray(f.RUNS) ? f.RUNS : [] + const runsResolved = runs.map((id) => ({ id, ok: seenSkill.has(id) })) + const unresolved = runsResolved.filter((r) => !r.ok) + const messages = [ + ...(code ? [] : [`no job ${f.ID} in public/crons/manifest.json`]), + ...unresolved.map((r) => `RUNS names ${r.id}, which has no skill spec`), + ] + let health = 'ok' + if (!code) health = 'warn' + if (unresolved.length) health = 'fail' + // The code catalog knows the schedule it read from the file; when the spec + // disagrees, that is a fact the reader must see, not a build failure. + if (code) { + const codeExpr = code.schedule?.expr ?? null + const codeMs = code.schedule?.everyMs ?? null + if (codeExpr && f.SCHEDULE !== undefined && f.SCHEDULE !== codeExpr) { messages.push(`SCHEDULE ${JSON.stringify(f.SCHEDULE)} differs from the code catalog (${codeExpr})`); if (health === 'ok') health = 'warn' } + if (codeMs && f.INTERVAL_MS !== undefined && f.INTERVAL_MS !== codeMs) { messages.push(`INTERVAL_MS ${f.INTERVAL_MS} differs from the code catalog (${codeMs})`); if (health === 'ok') health = 'warn' } + if (code.kind !== f.HOST) { messages.push(`HOST ${f.HOST} differs from the code catalog (${code.kind})`); if (health === 'ok') health = 'warn' } + } + crons.push({ + id: f.ID, + specPath: file, + sha256: c.sha256, + typecheckOk: c.verdict.typecheckOk, + discarded: c.verdict.discarded, + moduleName: c.moduleName, + inSpecCorpus: corpusPaths.has(file), + fields: f, + code: code ? { where: code.where, sourceUrl: code.sourceUrl ?? null, health: code.health, kind: code.kind, repoPrivate: code.repoPrivate === true } : null, + witness: code ? 'spec+code' : 'spec-only', + runs, + runsResolved, + control: f.CONTROL, + runNow: runNowTarget(f, railway), + health, + messages, + }) + } + + // The reverse link, from the crons' RUNS. + for (const cr of crons) for (const r of cr.runsResolved) { + if (!r.ok) continue + const sk = skills.find((s) => s.id === r.id) + if (sk && !sk.runBy.includes(cr.id)) sk.runBy.push(cr.id) + } + for (const sk of skills) sk.runBy.sort() + + const byId = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0) + skills.sort(byId) + crons.sort(byId) + const codeOnlySkills = [...codeSkills.keys()].filter((id) => !seenSkill.has(id)).sort() + const codeOnlyCrons = [...codeCrons.keys()].filter((id) => !seenCron.has(id)).sort() + + const base = { version: VERSION, compilerWasmSha256 } + const skillsOut = sortKeys({ + ...base, + generatedAt, + counts: { specs: skills.length, specPlusCode: skills.filter((s) => s.witness === 'spec+code').length, specOnly: skills.filter((s) => s.witness === 'spec-only').length, codeOnly: codeOnlySkills.length, typecheckOk: skills.filter((s) => s.typecheckOk).length, runBy: skills.filter((s) => s.runBy.length).length }, + skills, + codeOnly: codeOnlySkills, + }) + const cronsOut = sortKeys({ + ...base, + generatedAt, + counts: { specs: crons.length, specPlusCode: crons.filter((c) => c.witness === 'spec+code').length, specOnly: crons.filter((c) => c.witness === 'spec-only').length, codeOnly: codeOnlyCrons.length, typecheckOk: crons.filter((c) => c.typecheckOk).length, withRuns: crons.filter((c) => c.runs.length).length, byHost: Object.fromEntries(HOSTS.map((h) => [h, crons.filter((c) => c.fields.HOST === h).length])) }, + crons, + codeOnly: codeOnlyCrons, + }) + // A hash of everything but the clock, so a checker can compare committed and + // regenerated output without the timestamp getting in the way. + skillsOut.contentSha256 = sha256(JSON.stringify({ ...skillsOut, generatedAt: null })) + cronsOut.contentSha256 = sha256(JSON.stringify({ ...cronsOut, generatedAt: null })) + return { skills: sortKeys(skillsOut), crons: sortKeys(cronsOut), problems } +} + +export function sortKeys(value) { + if (Array.isArray(value)) return value.map(sortKeys) + if (value && typeof value === 'object') { + return Object.fromEntries(Object.keys(value).sort().map((k) => [k, sortKeys(value[k])])) + } + return value +} + +// --------------------------------------------------------------------------- +// The site. +// --------------------------------------------------------------------------- +function readSpecDir(dir) { + const abs = join(SITE, CORPUS, dir) + if (!existsSync(abs)) return [] + return readdirSync(abs) + .filter((f) => f.endsWith('.t27')) + .sort() + .map((f) => ({ path: `${dir}/${f}`, text: readFileSync(join(abs, f), 'utf8') })) +} + +const readJson = (rel) => (existsSync(join(SITE, rel)) ? JSON.parse(readFileSync(join(SITE, rel), 'utf8')) : null) + +export async function generate({ generatedAt } = {}) { + const wasmBytes = readFileSync(join(SITE, WASM)) + const analyze = await loadCompiler(wasmBytes) + const skillSpecs = analyzeSpecFiles(analyze, readSpecDir(SKILL_SPEC_DIR)) + const cronSpecs = analyzeSpecFiles(analyze, readSpecDir(CRON_SPEC_DIR)) + const epoch = process.env.SOURCE_DATE_EPOCH + const stamp = generatedAt ?? (epoch ? new Date(Number(epoch) * 1000).toISOString() : new Date().toISOString()) + return buildSpecCatalogs({ + skillSpecs, + cronSpecs, + skillsManifest: readJson('public/skills/manifest.json'), + cronsManifest: readJson('public/crons/manifest.json'), + t27Manifest: readJson('public/t27/manifest.json'), + railway: readJson('scripts/railway-services.json'), + compilerWasmSha256: sha256(wasmBytes), + generatedAt: stamp, + }) +} + +function writeAtomic(rel, data) { + const dest = join(SITE, rel) + mkdirSync(dirname(dest), { recursive: true }) + const temp = `${dest}.${process.pid}.tmp` + writeFileSync(temp, `${JSON.stringify(data)}\n`, { flag: 'wx' }) + renameSync(temp, dest) +} + +async function main(argv) { + const check = argv.includes('--check') + const { skills, crons, problems } = await generate() + if (problems.length) { + console.error(`agents-from-specs: ${problems.length} problem(s)`) + for (const p of problems) console.error(' ' + p) + process.exit(1) + } + if (check) { + const prior = { skills: readJson(SKILLS_OUT), crons: readJson(CRONS_OUT) } + const drift = [] + if (prior.skills?.contentSha256 !== skills.contentSha256) drift.push(SKILLS_OUT) + if (prior.crons?.contentSha256 !== crons.contentSha256) drift.push(CRONS_OUT) + if (drift.length) { + console.error(`agents-from-specs: committed output is stale: ${drift.join(', ')} -- run node scripts/agents-from-specs.mjs`) + process.exit(1) + } + console.log(`agents-from-specs: ${SKILLS_OUT} and ${CRONS_OUT} match the specs`) + return + } + writeAtomic(SKILLS_OUT, skills) + writeAtomic(CRONS_OUT, crons) + const s = skills.counts, c = crons.counts + console.log(`agents-from-specs: skills ${s.specs} specs (typecheck ok ${s.typecheckOk}/${s.specs}; spec+code ${s.specPlusCode}, spec-only ${s.specOnly}, code-only ${s.codeOnly}) -> ${SKILLS_OUT}`) + console.log(`agents-from-specs: crons ${c.specs} specs (typecheck ok ${c.typecheckOk}/${c.specs}; spec+code ${c.specPlusCode}, spec-only ${c.specOnly}, code-only ${c.codeOnly}; with RUNS ${c.withRuns}) -> ${CRONS_OUT}`) +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).catch((e) => { console.error(e); process.exit(1) }) +} diff --git a/apps/website/scripts/agents-from-specs.test.mjs b/apps/website/scripts/agents-from-specs.test.mjs new file mode 100644 index 0000000000..ee4bf43396 --- /dev/null +++ b/apps/website/scripts/agents-from-specs.test.mjs @@ -0,0 +1,201 @@ +// What the spec→catalog generator refuses, and what it lets through. +// +// The interesting cases are the failures: two specs claiming one ID, a cron +// whose RUNS names a skill that has no spec, a spec with no code behind it, a +// spec the compiler does not accept. And the two quirks the generator leans +// on -- array literals arriving as identifiers and UTF-8 arriving as bytes -- +// are pinned here against the REAL wasm so a compiler update that changes +// either shape fails loudly instead of shipping mojibake. +// +// node --test scripts/agents-from-specs.test.mjs + +import test from 'node:test' +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { + SITE, analyzeSpecFiles, buildSpecCatalogs, constsOf, decodeBytes, loadCompiler, runNowTarget, sortKeys, verdictOf, +} from './agents-from-specs.mjs' + +const analyze = await loadCompiler(readFileSync(join(SITE, 'public/t27/t27_compiler.wasm'))) + +const q = JSON.stringify +const skillSrc = (id, { module = 'skill_x', specs = [], extra = '' } = {}) => `module ${module}; +pub const KIND : str = "skill"; +pub const ID : str = ${q(id)}; +pub const NAME : str = "x"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "en"; +pub const SUMMARY_RU : str = "Проверка ✓ ⟲ ◷"; +pub const COMMAND : str = "/x"; +pub const SPECS : [${specs.length}]str = ${q(specs)}; +pub const TAGS : [1]str = ["t"]; +pub const ENABLED : bool = true; +pub const TIMEOUT_MIN : u16 = 30; +${extra}` +const cronSrc = (id, { module = 'cron_x', runs = [], host = 'github-actions', schedule = '"0 0 * * *"', extra = '' } = {}) => `module ${module}; +pub const KIND : str = "cron"; +pub const ID : str = ${q(id)}; +pub const NAME : str = "x"; +pub const HOST : str = ${q(host)}; +pub const REPO : str = "trinity"; +pub const SERVICE : str = ".github/workflows/x.yml"; +pub const SUMMARY_EN : str = "en"; +pub const SUMMARY_RU : str = "ru"; +${host === 'timer' ? 'pub const INTERVAL_MS : u32 = 5000;' : `pub const SCHEDULE : str = ${schedule};`} +pub const TZ : str = "UTC"; +pub const RUNS : [${runs.length}]str = ${q(runs)}; +pub const RUNS_NOTE : str = "test"; +pub const ENABLED : bool = true; +pub const ON_FAILURE : str = "log"; +pub const CONTROL : str = "code-only"; +${extra}` + +const files = (dir, ...srcs) => srcs.map((text, i) => ({ path: `${dir}/x${i}.t27`, text })) +const skillsManifest = { skills: [{ id: 'trinity/x', path: 'trinity/x/SKILL.md', sha256: 'a'.repeat(64), health: 'ok', link: 'unbound' }] } +const cronsManifest = { crons: [{ id: 'github-actions/trinity/x', kind: 'github-actions', schedule: { kind: 'cron', expr: '0 0 * * *' }, where: { file: 'w.yml', line: 1 }, health: 'ok' }] } +const build = (skillSpecs, cronSpecs, over = {}) => buildSpecCatalogs({ + skillSpecs: analyzeSpecFiles(analyze, skillSpecs), + cronSpecs: analyzeSpecFiles(analyze, cronSpecs), + skillsManifest, cronsManifest, t27Manifest: { specs: [{ path: 'specs/demos/hello_world.t27' }] }, + compilerWasmSha256: 'w', generatedAt: '2026-01-01T00:00:00.000Z', ...over, +}) + +test('the compiler accepts the schema, including an empty array, and constants come back typed', () => { + const a = analyze(skillSrc('trinity/x', { module: 'skill_x0' })) + const v = verdictOf(a) + assert.equal(v.typecheckOk, true) + assert.equal(v.discarded, 0) + const c = constsOf(a) + assert.equal(c.ID.value, 'trinity/x') + assert.deepEqual(c.SPECS.value, []) + assert.equal(c.ENABLED.value, true) + assert.equal(c.TIMEOUT_MIN.value, 30) + assert.equal(c.TIMEOUT_MIN.type, 'u16') +}) + +test('array literals arrive as identifiers and UTF-8 arrives as bytes; both are undone exactly', () => { + const a = analyze(skillSrc('trinity/x', { module: 'skill_x0', specs: ['specs/a.t27', 'specs/б.t27'] })) + const arr = a.ast.children.find((n) => n.name === 'SPECS').children[0] + assert.equal(arr.kind, 'ExprIdentifier', 'the quirk this generator documents: array literal in an identifier name') + const c = constsOf(a) + assert.deepEqual(c.SPECS.value, ['specs/a.t27', 'specs/б.t27']) + assert.equal(c.SUMMARY_RU.value, 'Проверка ✓ ⟲ ◷') + assert.equal(decodeBytes('plain ascii'), 'plain ascii') + assert.equal(decodeBytes('уже верно'), 'уже верно', 'text that is already decoded is left alone') +}) + +test('a clean pair is spec+code, healthy, with the reverse link filled in', () => { + const r = build(files('specs/skills', skillSrc('trinity/x', { module: 'skill_x0' })), files('specs/crons', cronSrc('github-actions/trinity/x', { module: 'cron_x0', runs: ['trinity/x'] }))) + assert.deepEqual(r.problems, []) + assert.equal(r.skills.skills[0].witness, 'spec+code') + assert.equal(r.crons.crons[0].witness, 'spec+code') + assert.equal(r.crons.crons[0].health, 'ok') + assert.deepEqual(r.crons.crons[0].runsResolved, [{ id: 'trinity/x', ok: true }]) + assert.deepEqual(r.skills.skills[0].runBy, ['github-actions/trinity/x']) + assert.deepEqual(r.skills.codeOnly, []) + assert.equal(r.skills.counts.runBy, 1) +}) + +test('duplicate IDs fail the build', () => { + const r = build(files('specs/skills', skillSrc('trinity/x', { module: 'skill_x0' }), skillSrc('trinity/x', { module: 'skill_x1' })), []) + assert.ok(r.problems.some((p) => p.includes('duplicate skill ID trinity/x')), r.problems.join('\n')) +}) + +test('a RUNS entry with no skill spec marks the cron fail, with a message', () => { + const r = build([], files('specs/crons', cronSrc('github-actions/trinity/x', { module: 'cron_x0', runs: ['trinity/ghost'] }))) + assert.deepEqual(r.problems, []) + const c = r.crons.crons[0] + assert.equal(c.health, 'fail') + assert.deepEqual(c.runsResolved, [{ id: 'trinity/ghost', ok: false }]) + assert.ok(c.messages.some((m) => m.includes('trinity/ghost'))) +}) + +test('a spec the code catalog does not know is spec-only and warns; a code entry with no spec is code-only', () => { + const r = build(files('specs/skills', skillSrc('trinity/only-in-spec', { module: 'skill_x0' })), []) + assert.equal(r.skills.skills[0].witness, 'spec-only') + assert.equal(r.skills.skills[0].health, 'warn') + assert.deepEqual(r.skills.codeOnly, ['trinity/x']) + assert.deepEqual(r.crons.codeOnly, ['github-actions/trinity/x']) + assert.equal(r.crons.counts.codeOnly, 1) +}) + +test('the module name must follow the file name', () => { + const r = build(files('specs/skills', skillSrc('trinity/x', { module: 'skill_wrong' })), []) + assert.ok(r.problems.some((p) => p.includes('module must be skill_x0'))) +}) + +test('a timer carries INTERVAL_MS, a schedule carries SCHEDULE, never both', () => { + const r = build([], files('specs/crons', + cronSrc('timer/trinity/t', { module: 'cron_x0', host: 'timer', extra: 'pub const SCHEDULE : str = "* * * * *";' }), + cronSrc('github-actions/trinity/x', { module: 'cron_x1', extra: 'pub const INTERVAL_MS : u32 = 1;' }), + )) + assert.ok(r.problems.some((p) => p.includes('x0.t27: a timer has INTERVAL_MS, not SCHEDULE')), r.problems.join('\n')) + assert.ok(r.problems.some((p) => p.includes('x1.t27: only a timer has INTERVAL_MS')), r.problems.join('\n')) +}) + +test('a spec that disagrees with the schedule the code catalog read is shown, as warn', () => { + const r = build([], files('specs/crons', cronSrc('github-actions/trinity/x', { module: 'cron_x0', schedule: '"5 5 * * *"' }))) + assert.deepEqual(r.problems, []) + assert.equal(r.crons.crons[0].health, 'warn') + assert.ok(r.crons.crons[0].messages.some((m) => m.includes('differs from the code catalog'))) +}) + +test('unknown constants, wrong shapes and bad enums are problems, not silently dropped', () => { + const r = build( + files('specs/skills', skillSrc('trinity/x', { module: 'skill_x0', extra: 'pub const EXTRA : str = "x";' })), + files('specs/crons', cronSrc('github-actions/trinity/x', { module: 'cron_x0', extra: '' }).replace('"code-only"', '"telepathy"')), + ) + assert.ok(r.problems.some((p) => p.includes('unknown constant EXTRA'))) + assert.ok(r.problems.some((p) => p.includes('CONTROL telepathy'))) +}) + +test('the wasm typecheck is lenient, so the schema is the gate: wrong annotation, shape, length and range are all problems', () => { + // Measured against the vendored wasm: each of these still gets typecheck.ok === true. + const src = `module skill_x0; +pub const KIND : str = 5; +pub const ID : str = "trinity/x"; +pub const NAME : str = "x"; +pub const REPO : str = "trinity"; +pub const SOURCE : str = "SKILL.md"; +pub const SUMMARY_EN : str = "en"; +pub const SUMMARY_RU : str = "ru"; +pub const COMMAND : str = ; +pub const SPECS : [2]str = ["a"]; +pub const TAGS : [1]str = ["t"]; +pub const ENABLED : bool = "yes"; +pub const TIMEOUT_MIN : u16 = 70000; +` + const a = analyze(src) + assert.equal(a.typecheck.ok, true, 'if this starts failing the compiler got stricter; the schema gate below stays') + const r = build(files('specs/skills', src), []) + const text = r.problems.join('\n') + assert.match(text, /KIND: value is not a string literal/) + assert.match(text, /missing COMMAND/) + assert.match(text, /SPECS: annotated \[2\]str, holds 1 string/) + assert.match(text, /ENABLED: value is not true\/false/) + assert.match(text, /TIMEOUT_MIN: value 70000 is not in u16 range/) +}) + +test('output is deterministic: sorted keys, sorted ids, content hash independent of the clock', () => { + const a = build(files('specs/skills', skillSrc('trinity/x', { module: 'skill_x0' })), []) + const b = build(files('specs/skills', skillSrc('trinity/x', { module: 'skill_x0' })), [], { generatedAt: '2030-01-01T00:00:00.000Z' }) + assert.equal(a.skills.contentSha256, b.skills.contentSha256) + assert.deepEqual(Object.keys(a.skills), Object.keys(a.skills).slice().sort()) + assert.deepEqual(sortKeys({ b: 1, a: [{ d: 1, c: 2 }] }), { a: [{ c: 2, d: 1 }], b: 1 }) +}) + +test('runNowTarget: every host resolves to the panel that owns it, and a timer to nothing', () => { + const railway = { projectId: 'P', services: [{ name: 'jobsearch-cron', serviceId: 'S' }] } + assert.deepEqual(runNowTarget({ HOST: 'github-actions', REPO: 'trinity', SERVICE: '.github/workflows/site-live-gate.yml' }, railway), { + kind: 'link', via: 'github-actions', url: 'https://github.com/gHashTag/trinity/actions/workflows/site-live-gate.yml', + }) + assert.deepEqual(runNowTarget({ HOST: 'railway-cron', REPO: 'x', SERVICE: 'jobsearch-cron' }, railway), { + kind: 'link', via: 'railway', url: 'https://railway.com/project/P/service/S', + }) + assert.deepEqual(runNowTarget({ HOST: 'railway-cron', REPO: 'x', SERVICE: 'nobody' }, railway), { kind: 'disabled', reason: 'unknown-service' }) + assert.deepEqual(runNowTarget({ HOST: 'railway-cron', REPO: 'x', SERVICE: 'jobsearch-cron' }, null), { kind: 'disabled', reason: 'unknown-service' }) + assert.equal(runNowTarget({ HOST: 'inngest', REPO: 'x', SERVICE: 'y' }, railway).via, 'inngest') + assert.deepEqual(runNowTarget({ HOST: 'timer', REPO: 'x', SERVICE: 'y' }, railway), { kind: 'disabled', reason: 'timer' }) +}) diff --git a/apps/website/scripts/railway-services.json b/apps/website/scripts/railway-services.json index c05056fc0f..aa770632b2 100644 --- a/apps/website/scripts/railway-services.json +++ b/apps/website/scripts/railway-services.json @@ -1 +1 @@ -{"project":"999","services":[{"name":"jobsearch-cron","role":"cron","domains":["cron.t27.ai"]},{"name":"skills-site","role":"host","domains":["skills.t27.ai"]},{"name":"trios-agent-server","role":"host","domains":["api.t27.ai","trios-agent-server-production.up.railway.app"]},{"name":"vibee-render","role":"host","domains":["vibee-render-production.up.railway.app"]},{"name":"999-multibots-telegraf","role":"host","domains":[]},{"name":"inngest/inngest","role":"host","domains":[]},{"name":"vibee-editor","role":"host","domains":[]},{"name":"queen-ollama","role":"host","domains":[]},{"name":"queen-proxy","role":"host","domains":[]},{"name":"zep","role":"host","domains":[]},{"name":"zep-nlp","role":"host","domains":[]},{"name":"postgrest","role":"host","domains":[]},{"name":"supabase-gateway","role":"host","domains":[]},{"name":"t27-github-collab","role":"host","domains":[]},{"name":"trios-railway","role":"host","domains":[]},{"name":"Console","role":"host","domains":[]},{"name":"Postgres","role":"store","domains":[]},{"name":"Postgres-NFrq","role":"store","domains":[]},{"name":"pgvector","role":"store","domains":[]},{"name":"Redis","role":"store","domains":[]},{"name":"Bucket","role":"store","domains":[]}]} +{"project":"999","services":[{"name":"jobsearch-cron","role":"cron","domains":["cron.t27.ai"],"serviceId":"3196472e-b885-43e0-b553-0644664e4496"},{"name":"skills-site","role":"host","domains":["skills.t27.ai"],"serviceId":"84197d71-0e3a-4a35-a5ae-05c86b5cf57f"},{"name":"trios-agent-server","role":"host","domains":["api.t27.ai","trios-agent-server-production.up.railway.app"],"serviceId":"40d67e62-08bd-4eea-b709-7201242e9d82"},{"name":"vibee-render","role":"host","domains":["vibee-render-production.up.railway.app"]},{"name":"999-multibots-telegraf","role":"host","domains":[]},{"name":"inngest/inngest","role":"host","domains":[],"serviceId":"3cb0290e-0858-47e6-921d-edd10ed6de3c"},{"name":"vibee-editor","role":"host","domains":[]},{"name":"queen-ollama","role":"host","domains":[]},{"name":"queen-proxy","role":"host","domains":[]},{"name":"zep","role":"host","domains":[]},{"name":"zep-nlp","role":"host","domains":[]},{"name":"postgrest","role":"host","domains":[]},{"name":"supabase-gateway","role":"host","domains":[]},{"name":"t27-github-collab","role":"host","domains":[]},{"name":"trios-railway","role":"host","domains":[]},{"name":"Console","role":"host","domains":[]},{"name":"Postgres","role":"store","domains":[]},{"name":"Postgres-NFrq","role":"store","domains":[]},{"name":"pgvector","role":"store","domains":[]},{"name":"Redis","role":"store","domains":[]},{"name":"Bucket","role":"store","domains":[]}],"projectId":"564d9ebd-7aa8-44fe-93ec-e0b03c87158d"} From e10bd7bd9c10f8b13d287d0618e31a2060fc0045 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Wed, 9 Sep 2026 12:05:47 +0000 Subject: [PATCH 3/5] feat(explorers): spec panel first, cross-links, CONTROL strip (EN+RU) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skill and Cron Explorer cards open on the spec: its bytes (highlighted through the compiler), sha256 verified in the browser, the compiler verdict, and the witness label. Crons link to the skills their RUNS name (⟲ N), skills to the jobs that run them (◷ N). The CONTROL strip reads ENABLED from the spec, links to the canonical edit on gHashTag/t27 and the vendored copy, and sends 'run now' to the host that owns the job — GitHub Actions workflow page, Railway service, Inngest dashboard — while a timer says it has no outside handle. VITE_AGENT_CONTROL_URL (optional, documented in docs/agent-control-api.md) adds live buttons; unset, the strip says no control plane is connected. Witness and enabled join the tag filters. Cards without a spec say code-only. --- .gitignore | 4 + apps/website/.env.example.queen | 9 + apps/website/docs/agent-control-api.md | 55 +++ .../website/src/components/AgentSpecPanel.tsx | 444 ++++++++++++++++++ apps/website/src/lib/agentSpecs.ts | 196 ++++++++ apps/website/src/pages/CronExplorer.tsx | 77 ++- apps/website/src/pages/SkillExplorer.tsx | 87 +++- 7 files changed, 837 insertions(+), 35 deletions(-) create mode 100644 apps/website/docs/agent-control-api.md create mode 100644 apps/website/src/components/AgentSpecPanel.tsx create mode 100644 apps/website/src/lib/agentSpecs.ts diff --git a/.gitignore b/.gitignore index 7aab2b78f8..bad690c296 100644 --- a/.gitignore +++ b/.gitignore @@ -439,3 +439,7 @@ fpga-flash # Enumerating them one by one was tried first and is whack-a-mole: the widened # corpus immediately hit three more. !apps/website/public/t27/** + +# apps/website/docs: hand-written contracts for the website (e.g. the optional +# agent control API), not generated reports; the *.md rule above would drop them. +!apps/website/docs/** diff --git a/apps/website/.env.example.queen b/apps/website/.env.example.queen index 717cba9a71..287be00928 100644 --- a/apps/website/.env.example.queen +++ b/apps/website/.env.example.queen @@ -13,3 +13,12 @@ # Ollama also has to allow the browser's origin: OLLAMA_ORIGINS=http://localhost:4179 VITE_QUEEN_OLLAMA_URL=http://localhost:11434 # VITE_QUEEN_OLLAMA_MODEL=qwen3:4b + +# Optional live control plane for the Skill and Cron Explorers. Not a secret: +# the URL is compiled into the public bundle. When set, the CONTROL strip on a +# skill or job card gets working buttons that POST to +# {url}/crons//run | /enable | /disable and {url}/skills//run +# with the JSON body {"id", "requestedAt"} — see docs/agent-control-api.md. +# When unset (the default, and the deployed state today) the strip says so and +# every action goes through the spec and the hosts' own panels instead. +# VITE_AGENT_CONTROL_URL=https://example.invalid/agent-control diff --git a/apps/website/docs/agent-control-api.md b/apps/website/docs/agent-control-api.md new file mode 100644 index 0000000000..7a6c06ed84 --- /dev/null +++ b/apps/website/docs/agent-control-api.md @@ -0,0 +1,55 @@ +# Agent control API (optional control plane for skills and crons) + +Status: **contract only — no server implements this today.** The Skill and Cron +Explorers on t27.ai read their catalogs from `.t27` specs +(`public/t27/files/specs/{skills,crons}/*.t27` → `scripts/agents-from-specs.mjs` +→ `public/skills/spec-skills.json`, `public/crons/spec-crons.json`). The +management strip on each card works without any server: `ENABLED` is read from +the spec and changed by editing the spec (GitHub edit link), and "run now" +points at the host that actually owns the job (GitHub Actions workflow page, +Railway service, Inngest dashboard). A timer inside a process has no outside +handle and the strip says so. + +When `VITE_AGENT_CONTROL_URL` is set at build time, the strip additionally gets +live buttons that call the endpoints below. When it is unset the strip shows +"Контур управления не подключён — действия идут через спеку и панели хостов" / +"No control plane connected — actions go through the spec and the hosts' panels". + +## Endpoints + +All requests are `POST`, body `application/json`: + +```json +{ "id": "", "requestedAt": "" } +``` + +| Endpoint | Meaning | +|-------------------------------------------|----------------------------------------------------------------| +| `POST {url}/crons//run` | Start the job now (`` e.g. `github-actions/trinity/site-live-gate`) | +| `POST {url}/crons//enable` | Ask the plane to set the job's `ENABLED` to `true` | +| `POST {url}/crons//disable` | Ask the plane to set the job's `ENABLED` to `false` | +| `POST {url}/skills//run` | Launch the skill once (`` e.g. `trinity/doctor`) | + +`` is the catalog id with each `/`-separated segment URL-encoded; the +segments themselves are kept as path segments. + +## Responses + +- `202 Accepted` — the request was queued; body may carry `{ "runId": "..." }`. +- `200 OK` — for `enable`/`disable`, the new state was recorded. +- `404 Not Found` — the id names no known skill or job. +- `409 Conflict` — the job is a timer or otherwise has no outside handle. +- `501 Not Implemented` — the plane knows the id but cannot act on that host. + +Any non-2xx is shown verbatim (status and body) in the strip; nothing is +retried and nothing is reported as done that the server did not confirm. + +## Honesty rules the client keeps + +- `enable`/`disable` never edit the spec themselves; the spec remains the + source of truth and a plane that flips state must land the change as a + commit to `specs/crons/.t27`, or the site will keep showing the spec's + value. +- No credentials are sent by the browser (`credentials: 'omit'`); a plane that + needs auth must sit behind its own gateway. +- The URL is public (it is in the bundle); it must not embed a token. diff --git a/apps/website/src/components/AgentSpecPanel.tsx b/apps/website/src/components/AgentSpecPanel.tsx new file mode 100644 index 0000000000..f033ae9862 --- /dev/null +++ b/apps/website/src/components/AgentSpecPanel.tsx @@ -0,0 +1,444 @@ +// The spec side of a skill or cron card, shared by both Explorers. +// +// A skill or a job is a fact only when a .t27 spec states it; the code the +// site's sync scripts scanned is the witness that the fact is also deployed. +// This panel shows the spec itself (its bytes, its hash, the compiler's verdict +// on it), where it can be changed, where the job can be started, and how it is +// linked to its neighbours — without inventing a control it does not have: a +// timer inside a process has no outside handle, and a page with no control +// plane configured says so instead of pretending a button does something. +// +// The copy lives here rather than in each page's UI literal because the two +// pages must say exactly the same thing about the same spec. + +import { useCallback, useEffect, useMemo, useState } from 'react' +import { SpecCodeView } from './SpecCodeView' +import { C, pill, tagChip } from '../lib/explorerTheme' +import { highlightCode, highlightSource, type Span } from '../lib/highlight' +import { analyze } from '../lib/t27Compiler' +import { specExplorerHash } from '../lib/specCatalog' +import { + agentControlUrl, + canonicalSpecEditUrl, + loadAgentSpecSource, + requestControl, + specSlug, + vendoredSpecUrl, + WITNESS_LABEL, + type ControlAction, + type CronSpecEntry, + type SkillSpecEntry, +} from '../lib/agentSpecs' + +const COPY = { + en: { + spec: 'SPEC', + control: 'CONTROL', + links: 'LINKS', + verdictOk: 'typecheck ok', + verdictFail: 'typecheck failed', + verdictNote: 'the compiler accepted the module; the field schema is checked by the generator, and both must hold', + discarded: 'discarded', + sha: 'sha256', + shaVerified: 'bytes match the catalog hash', + shaMismatch: 'served bytes differ from the catalog hash — the vendored copy and the generated JSON are out of step', + shaUnchecked: 'hash not verified here (no WebCrypto)', + openSpec: 'Open in Spec Explorer →', + notInCorpus: 'not in the vendored corpus manifest yet (sync-t27-specs has not been re-run)', + loading: 'loading the spec…', + failed: 'could not load the spec:', + codeOnly: 'This card is built from code only: no .t27 spec states it yet. The fields shown come from the sync script’s scan, not from a spec.', + createSpec: 'Write the spec →', + enabled: 'ENABLED', + on: 'true', + off: 'false', + readOnly: 'read from the spec; change it there', + editSpec: 'Edit in the spec →', + vendored: 'vendored copy', + runNow: 'Run now →', + viaActions: 'GitHub Actions workflow page', + viaRailway: 'Railway service', + viaInngest: 'Inngest dashboard', + timerDisabled: 'timer inside the process — controlled by the service’s deployment', + unknownService: 'the Railway service id is not known to the site', + skillRun: 'a skill is launched by an agent with its command; there is no run button without a control plane', + command: 'command', + noPlane: 'No control plane connected — actions go through the spec and the hosts’ panels', + plane: 'control plane', + actRun: 'run', + actEnable: 'enable', + actDisable: 'disable', + sent: 'accepted', + rejected: 'rejected', + runs: 'runs skills', + runBy: 'run by jobs', + noRuns: 'names no skill', + noRunBy: 'no job names this skill', + unresolved: 'unresolved id', + summary: 'summary', + copy: 'copy', + copied: 'copied', + messages: 'generator notes', + }, + ru: { + spec: 'СПЕКА', + control: 'УПРАВЛЕНИЕ', + links: 'СВЯЗИ', + verdictOk: 'типизация ок', + verdictFail: 'типизация не прошла', + verdictNote: 'компилятор принял модуль; схему полей проверяет генератор, и нужны оба', + discarded: 'отброшено', + sha: 'sha256', + shaVerified: 'байты совпадают с хешем каталога', + shaMismatch: 'отданные байты не совпадают с хешем каталога — вендорная копия и сгенерированный JSON разошлись', + shaUnchecked: 'хеш здесь не проверен (нет WebCrypto)', + openSpec: 'Открыть в Обозревателе спек →', + notInCorpus: 'ещё не в манифесте вендорного корпуса (sync-t27-specs не перезапускался)', + loading: 'загружаем спеку…', + failed: 'не удалось загрузить спеку:', + codeOnly: 'Эта карточка построена только из кода: спеки .t27 для неё ещё нет. Поля взяты из сканирования скриптом синхронизации, а не из спеки.', + createSpec: 'Написать спеку →', + enabled: 'ENABLED', + on: 'true', + off: 'false', + readOnly: 'читается из спеки; менять там', + editSpec: 'Изменить в спеке →', + vendored: 'вендорная копия', + runNow: 'Запустить сейчас →', + viaActions: 'страница workflow в GitHub Actions', + viaRailway: 'сервис в Railway', + viaInngest: 'панель Inngest', + timerDisabled: 'таймер внутри процесса — управляется деплоем сервиса', + unknownService: 'id сервиса Railway сайту не известен', + skillRun: 'скилл запускает агент по его команде; без контура управления кнопки запуска нет', + command: 'команда', + noPlane: 'Контур управления не подключён — действия идут через спеку и панели хостов', + plane: 'контур управления', + actRun: 'запустить', + actEnable: 'включить', + actDisable: 'выключить', + sent: 'принято', + rejected: 'отклонено', + runs: 'запускает скиллы', + runBy: 'запускается заданиями', + noRuns: 'не называет ни одного скилла', + noRunBy: 'ни одно задание не называет этот скилл', + unresolved: 'неизвестный id', + summary: 'кратко', + copy: 'копировать', + copied: 'скопировано', + messages: 'заметки генератора', + }, +} as const + +type Copy = Record + +export interface CrossLink { + id: string + href: string + ok: boolean +} + +interface Props { + lang: 'en' | 'ru' + kind: 'skill' | 'cron' + /** The catalog id of the card, for the code-only case where there is no spec. */ + id: string + entry: SkillSpecEntry | CronSpecEntry | null + links: CrossLink[] + /** Whether the reader can jump to the Spec Explorer in this frame. */ + embedded: boolean +} + +async function sha256Hex(text: string): Promise { + const subtle = globalThis.crypto?.subtle + if (!subtle) return null + const digest = await subtle.digest('SHA-256', new TextEncoder().encode(text)) + return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('') +} + +function label(kind: Props['kind'], t: Copy): { links: string; empty: string } { + return kind === 'cron' ? { links: t.runs, empty: t.noRuns } : { links: t.runBy, empty: t.noRunBy } +} + +export function AgentSpecPanel({ lang, kind, id, entry, links, embedded }: Props) { + const t: Copy = COPY[lang] + // Everything fetched for one spec travels together, keyed by its path, so a + // change of card is a change of key rather than a burst of resets. + interface Loaded { path: string; source: string; lines: Span[][]; err: string | null; sha: 'pending' | 'ok' | 'mismatch' | 'unchecked' } + const [loaded, setLoaded] = useState(null) + const [copied, setCopied] = useState(false) + const [planeNote, setPlaneNote] = useState(null) + const plane = agentControlUrl() + + const specPath = entry?.specPath ?? null + const expectedSha = entry?.sha256 ?? null + useEffect(() => { + if (!specPath || !expectedSha) return + let alive = true + const path = specPath + loadAgentSpecSource(path) + .then(async (text) => { + if (!alive) return + const hex = await sha256Hex(text) + const sha: Loaded['sha'] = hex === null ? 'unchecked' : hex === expectedSha ? 'ok' : 'mismatch' + let lines: Span[][] + try { + lines = highlightSource(text, (await analyze(text)).tokens) + } catch { + lines = highlightCode(text, 't27') + } + if (alive) setLoaded({ path, source: text, lines, err: null, sha }) + }) + .catch((e) => alive && setLoaded({ path, source: '', lines: [], err: String(e instanceof Error ? e.message : e), sha: 'unchecked' })) + return () => { + alive = false + } + }, [specPath, expectedSha]) + + const current = loaded && loaded.path === specPath ? loaded : null + const source = current?.source ?? '' + const lines = current?.lines ?? [] + const err = current?.err ?? null + const shaState = current?.sha ?? 'pending' + + const copySha = useCallback(() => { + if (!entry) return + navigator.clipboard?.writeText(entry.sha256).then( + () => { + setCopied(true) + window.setTimeout(() => setCopied(false), 1400) + }, + () => {}, + ) + }, [entry]) + + const act = useCallback( + async (action: ControlAction) => { + if (!plane) return + setPlaneNote('…') + try { + const r = await requestControl(plane, kind, id, action) + setPlaneNote(`${r.ok ? t.sent : t.rejected} · HTTP ${r.status}${r.body ? ` · ${r.body.slice(0, 160)}` : ''}`) + } catch (e) { + setPlaneNote(`${t.rejected} · ${String(e instanceof Error ? e.message : e)}`) + } + }, + [plane, kind, id, t.sent, t.rejected], + ) + + const heading = (text: string) =>
{text}
+ const box: React.CSSProperties = { background: C.panel, border: `1px solid ${C.border}`, borderRadius: 6, padding: 12, display: 'flex', flexDirection: 'column', gap: 8 } + + const runNow = useMemo(() => { + if (!entry || kind !== 'cron') return null + return (entry as CronSpecEntry).runNow + }, [entry, kind]) + + const summary = entry ? (lang === 'ru' ? entry.fields.SUMMARY_RU : entry.fields.SUMMARY_EN) : '' + const { links: linksLabel, empty: emptyLabel } = label(kind, t) + + // ---- code-only: no spec to show, but an honest card and a way to write one. + if (!entry) { + const dir = kind === 'cron' ? 'specs/crons' : 'specs/skills' + const filename = `${id.replace(/[^A-Za-z0-9_.-]+/g, '-')}.t27` + return ( +
+
+
+ {heading(t.spec)} + {WITNESS_LABEL['code-only'][lang]} +
+
{t.codeOnly}
+ +
+
+ {heading(t.control)} +
{t.noPlane}
+
+
+ ) + } + + const witnessColor = entry.witness === 'spec+code' ? C.accent : C.warn + const verdictColor = entry.typecheckOk && entry.discarded === 0 ? C.accent : C.bad + + return ( +
+ {/* ---- the spec itself ---- */} +
+
+ {heading(t.spec)} + + {entry.specPath} + + {WITNESS_LABEL[entry.witness][lang]} + + {entry.typecheckOk ? t.verdictOk : t.verdictFail} + {entry.discarded > 0 ? ` · ${t.discarded} ${entry.discarded}` : ''} + +
+ {summary && ( +
+ {summary} +
+ )} +
+ {t.sha} + + {entry.sha256.slice(0, 16)}… + + + + {shaState === 'ok' ? `✓ ${t.shaVerified}` : shaState === 'mismatch' ? `✕ ${t.shaMismatch}` : shaState === 'unchecked' ? t.shaUnchecked : ''} + +
+
+ {entry.inSpecCorpus ? ( + + {t.openSpec} + + ) : ( + {t.notInCorpus} + )} +
+ {entry.messages.length > 0 && ( +
+ {t.messages}: + {entry.messages.join(' · ')} +
+ )} +
+ {err ? ( +
+ {t.failed} {err} +
+ ) : source ? ( + + ) : ( +
{t.loading}
+ )} +
+
+ + {/* ---- cross links ---- */} +
+
+ {heading(t.links)} + {linksLabel} +
+ {links.length === 0 ? ( +
+ {emptyLabel} + {kind === 'cron' && (entry as CronSpecEntry).fields.RUNS_NOTE ? ( + — {(entry as CronSpecEntry).fields.RUNS_NOTE} + ) : null} +
+ ) : ( +
+ {links.map((l) => + l.ok ? ( + + {kind === 'cron' ? '⟲' : '◷'} {l.id} + + ) : ( + + ✕ {l.id} + + ), + )} +
+ )} +
+ + {/* ---- management strip ---- */} +
+ {heading(t.control)} +
+ + {t.enabled} = {entry.fields.ENABLED ? t.on : t.off} + + + {t.editSpec} + + + {t.vendored} + +
+
+ {kind === 'cron' && runNow ? ( + runNow.kind === 'link' ? ( + <> + + {t.runNow} + + + {runNow.via === 'github-actions' ? t.viaActions : runNow.via === 'railway' ? t.viaRailway : t.viaInngest} + + + ) : ( + <> + + {t.runNow} + + {runNow.reason === 'timer' ? t.timerDisabled : t.unknownService} + + ) + ) : ( + + {t.skillRun} + {(entry as SkillSpecEntry).fields.COMMAND ? ( + <> + {' · '} + {t.command}: {(entry as SkillSpecEntry).fields.COMMAND} + + ) : null} + + )} +
+ {plane ? ( +
+ {t.plane} + + {kind === 'cron' && ( + <> + + + + )} + {planeNote && ( + + {planeNote} + + )} +
+ ) : ( +
{t.noPlane}
+ )} +
+ {specSlug(entry.specPath)} +
+
+
+ ) +} diff --git a/apps/website/src/lib/agentSpecs.ts b/apps/website/src/lib/agentSpecs.ts new file mode 100644 index 0000000000..e7a68dd0e9 --- /dev/null +++ b/apps/website/src/lib/agentSpecs.ts @@ -0,0 +1,196 @@ +// The spec-first side of the skills and crons catalogs. +// +// public/skills/spec-skills.json and public/crons/spec-crons.json are written +// at prebuild by scripts/agents-from-specs.mjs from the .t27 files under +// public/t27/files/specs/{skills,crons}/, through the real compiler. This file +// is the one place the page reads them, and the one place the management +// strip's facts are derived: where the canonical spec is edited, where the +// vendored copy lives, where "run now" goes for each host, and whether a live +// control plane is configured at all. Every helper is a pure function of the +// entry and the environment; nothing here guesses a URL the spec does not +// justify. + +export type Witness = 'spec+code' | 'spec-only' | 'code-only' +export type Health = 'ok' | 'warn' | 'fail' +export type CronHostKind = 'github-actions' | 'inngest' | 'railway-cron' | 'timer' +export type CronControl = 'github-actions-dispatch' | 'railway-dashboard' | 'inngest-dashboard' | 'code-only' + +export interface SkillSpecFields { + KIND: 'skill' + ID: string + NAME: string + REPO: string + SOURCE: string + SUMMARY_EN: string + SUMMARY_RU: string + COMMAND: string + SPECS: string[] + TAGS: string[] + ENABLED: boolean + TIMEOUT_MIN: number +} + +export interface CronSpecFields { + KIND: 'cron' + ID: string + NAME: string + HOST: CronHostKind + REPO: string + SERVICE: string + SUMMARY_EN: string + SUMMARY_RU: string + SCHEDULE?: string + SCHEDULE_NOTE?: string + INTERVAL_MS?: number + TZ: string + RUNS: string[] + RUNS_NOTE: string + ENABLED: boolean + NOTE?: string + ON_FAILURE: 'issue' | 'log' | 'unknown' + CONTROL: CronControl +} + +interface SpecEntryBase { + id: string + /** Corpus-relative, e.g. `specs/skills/trinity-doctor.t27`. */ + specPath: string + sha256: string + typecheckOk: boolean + discarded: number + moduleName: string | null + /** Whether public/t27/manifest.json (the vendored corpus index) lists this path yet. */ + inSpecCorpus: boolean + witness: Exclude + health: Health + messages: string[] +} + +export interface SkillSpecEntry extends SpecEntryBase { + fields: SkillSpecFields + code: { path: string; sha256: string; health: Health; link: string } | null + /** Cron ids whose RUNS name this skill. */ + runBy: string[] +} + +export interface CronSpecEntry extends SpecEntryBase { + fields: CronSpecFields + code: { where: { file: string | null; line: number | null; host: string | null; service: string | null }; sourceUrl: string | null; health: Health; kind: CronHostKind; repoPrivate: boolean } | null + runs: string[] + runsResolved: { id: string; ok: boolean }[] + control: CronControl + /** Resolved at build time by the generator from scripts/railway-services.json. */ + runNow: RunNowTarget +} + +interface SpecCatalogBase { + version: number + generatedAt: string + compilerWasmSha256: string + contentSha256: string + codeOnly: string[] +} + +export interface SkillSpecCatalog extends SpecCatalogBase { + counts: { specs: number; specPlusCode: number; specOnly: number; codeOnly: number; typecheckOk: number; runBy: number } + skills: SkillSpecEntry[] +} + +export interface CronSpecCatalog extends SpecCatalogBase { + counts: { specs: number; specPlusCode: number; specOnly: number; codeOnly: number; typecheckOk: number; withRuns: number; byHost: Record } + crons: CronSpecEntry[] +} + +let skillsPromise: Promise | null = null +let cronsPromise: Promise | null = null + +export function loadSkillSpecs(): Promise { + if (!skillsPromise) { + skillsPromise = fetch('skills/spec-skills.json', { credentials: 'omit' }).then((r) => { + if (!r.ok) throw new Error(`could not load spec-skills (${r.status})`) + return r.json() as Promise + }) + skillsPromise.catch(() => { skillsPromise = null }) + } + return skillsPromise +} + +export function loadCronSpecs(): Promise { + if (!cronsPromise) { + cronsPromise = fetch('crons/spec-crons.json', { credentials: 'omit' }).then((r) => { + if (!r.ok) throw new Error(`could not load spec-crons (${r.status})`) + return r.json() as Promise + }) + cronsPromise.catch(() => { cronsPromise = null }) + } + return cronsPromise +} + +/** The spec's own bytes, from the vendored corpus, checked against the catalog's hash when the page can. */ +export async function loadAgentSpecSource(specPath: string): Promise { + const r = await fetch(`t27/files/${specPath}`, { credentials: 'omit' }) + if (!r.ok) throw new Error(`could not load ${specPath} (${r.status})`) + return r.text() +} + +// --------------------------------------------------------------------------- +// Where things are. +// --------------------------------------------------------------------------- +/** gHashTag/t27's default branch, checked with `gh api repos/gHashTag/t27 --jq .default_branch` on 2026-09-09. */ +export const T27_DEFAULT_BRANCH = 'master' + +export function specSlug(specPath: string): string { + return specPath.replace(/^specs\/(skills|crons)\//, '').replace(/\.t27$/, '') +} + +/** The canonical spec: the file in gHashTag/t27, opened in GitHub's editor. */ +export function canonicalSpecEditUrl(specPath: string): string { + return `https://github.com/gHashTag/t27/edit/${T27_DEFAULT_BRANCH}/${specPath}` +} + +/** The vendored copy the site actually serves. */ +export function vendoredSpecUrl(specPath: string): string { + return `https://github.com/gHashTag/trinity/blob/main/apps/website/public/t27/files/${specPath}` +} + +export type RunNowTarget = + | { kind: 'link'; url: string; via: 'github-actions' | 'railway' | 'inngest' } + | { kind: 'disabled'; reason: 'timer' | 'unknown-service' } + +// --------------------------------------------------------------------------- +// The optional live control plane. See docs/agent-control-api.md. +// --------------------------------------------------------------------------- +export function agentControlUrl(): string | null { + const raw = (import.meta.env?.VITE_AGENT_CONTROL_URL as string | undefined) ?? '' + const url = raw.trim().replace(/\/+$/, '') + return url.length > 0 ? url : null +} + +export type ControlAction = 'run' | 'enable' | 'disable' + +export function controlEndpoint(base: string, kind: 'skill' | 'cron', id: string, action: ControlAction): string { + return `${base}/${kind === 'skill' ? 'skills' : 'crons'}/${id.split('/').map(encodeURIComponent).join('/')}/${action}` +} + +export async function requestControl(base: string, kind: 'skill' | 'cron', id: string, action: ControlAction): Promise<{ ok: boolean; status: number; body: string }> { + const r = await fetch(controlEndpoint(base, kind, id, action), { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ id, requestedAt: new Date().toISOString() }), + credentials: 'omit', + }) + return { ok: r.ok, status: r.status, body: await r.text().catch(() => '') } +} + +// --------------------------------------------------------------------------- +// Labels shared by both Explorers. +// --------------------------------------------------------------------------- +export const WITNESS_LABEL: Record = { + 'spec+code': { en: 'spec+code', ru: 'спека+код' }, + 'spec-only': { en: 'spec-only', ru: 'только спека' }, + 'code-only': { en: 'code-only', ru: 'только код' }, +} + +export function witnessOf(specById: Map, id: string): Witness { + return specById.has(id) ? 'spec+code' : 'code-only' +} diff --git a/apps/website/src/pages/CronExplorer.tsx b/apps/website/src/pages/CronExplorer.tsx index af25654bb9..68884702b7 100644 --- a/apps/website/src/pages/CronExplorer.tsx +++ b/apps/website/src/pages/CronExplorer.tsx @@ -17,6 +17,9 @@ import { usePageMeta } from '../hooks/usePageMeta' import { useHashParams } from '../hooks/useHashParams' import { ExplorerHeader } from '../components/ExplorerHeader' import { ExplorerLibrary, type ExplorerItem } from '../components/ExplorerLibrary' +import { AgentSpecPanel } from '../components/AgentSpecPanel' +import { loadCronSpecs, type CronSpecCatalog, type CronSpecEntry } from '../lib/agentSpecs' +import { skillExplorerHash } from '../lib/skillsCatalog' import { C, panelBox, pill, tagChip, type Health } from '../lib/explorerTheme' import { canonicalCronUrl, cronExplorerHash, describeCron, describeInterval, nextRuns, resolveManifestCron } from '../lib/cronsCatalog' import { loadCronsManifest, type CronEntry, type CronsManifest } from '../lib/cronsLoader' @@ -148,8 +151,20 @@ const TAG_FAMILIES = [ { prefix: 'host/', label: 'host' }, { prefix: 'repo/', label: 'repository' }, { prefix: 'health/', label: 'health' }, + { prefix: 'witness/', label: 'witness' }, + { prefix: 'enabled/', label: 'enabled' }, ] +/** + * The spec-side tags a card gains: which witness backs it and whether its spec + * says it is on. Derived here so the library's tag panel doubles as the + * witness / enabled filter without a second widget. + */ +function specTags(spec: CronSpecEntry | undefined): string[] { + if (!spec) return ['witness/code-only'] + return [`witness/${spec.witness}`, `enabled/${spec.fields.ENABLED ? 'true' : 'false'}`] +} + const HEALTH_COLOR: Record = { ok: C.accent, warn: C.warn, fail: C.bad } function kindLabel(kind: string, ui: Ui): string { @@ -168,6 +183,7 @@ export default function CronExplorer() { const embedded = params.embedded const [manifest, setManifest] = useState(null) + const [specs, setSpecs] = useState(null) const [selected, setSelected] = useState(null) const [err, setErr] = useState(null) const [query, setQuery] = useState('') @@ -218,6 +234,9 @@ export default function CronExplorer() { setErr(String(e instanceof Error ? e.message : e)) setPane('detail') }) + loadCronSpecs() + .then((c) => alive && setSpecs(c)) + .catch(() => {/* the spec panel says code-only on its own */}) return () => { alive = false } @@ -228,30 +247,37 @@ export default function CronExplorer() { // `?? []` creates a new array on every render, which would make every useMemo // below re-run for nothing. const crons = useMemo(() => manifest?.crons ?? [], [manifest]) + const specById = useMemo(() => new Map((specs?.crons ?? []).map((c) => [c.id, c])), [specs]) + const tagsOf = useCallback((c: CronEntry) => [...c.tags, ...specTags(specById.get(c.id))], [specById]) const filtered = useMemo(() => { const q = query.trim().toLowerCase() return crons.filter((c) => { if (kind && c.kind !== kind) return false if (healthFilter !== 'all' && c.health !== healthFilter) return false - if (tagSel.length && !tagSel.every((t) => c.tags.includes(t))) return false + if (tagSel.length) { + const tags = tagsOf(c) + if (!tagSel.every((t) => tags.includes(t))) return false + } if (!q) return true + const spec = specById.get(c.id) return ( c.id.toLowerCase().includes(q) || c.name.toLowerCase().includes(q) || c.what.toLowerCase().includes(q) || (c.where.file ?? '').toLowerCase().includes(q) || - (c.schedule.expr ?? '').includes(q) + (c.schedule.expr ?? '').includes(q) || + (spec ? `${spec.fields.SUMMARY_EN} ${spec.fields.SUMMARY_RU} ${spec.runs.join(' ')}`.toLowerCase().includes(q) : false) ) }) - }, [crons, query, kind, healthFilter, tagSel]) + }, [crons, query, kind, healthFilter, tagSel, tagsOf, specById]) const tagCounts = useMemo(() => { const acc: Record = {} - for (const c of filtered) for (const t of c.tags) acc[t] = (acc[t] ?? 0) + 1 + for (const c of filtered) for (const t of tagsOf(c)) acc[t] = (acc[t] ?? 0) + 1 for (const t of Object.keys(manifest?.tags ?? {})) acc[t] = acc[t] ?? 0 return acc - }, [filtered, manifest]) + }, [filtered, manifest, tagsOf]) const scheduleText = useCallback( (entry: CronEntry): string => { @@ -265,15 +291,21 @@ export default function CronExplorer() { const items: ExplorerItem[] = useMemo( () => - filtered.map((c) => ({ - id: c.id, - title: c.name, - subtitle: `${kindLabel(c.kind, ui)} · ${scheduleText(c)}`, - health: c.health, - tags: c.tags, - haystack: `${c.id} ${c.name} ${c.what}`.toLowerCase(), - })), - [filtered, scheduleText, ui], + filtered.map((c) => { + const spec = specById.get(c.id) + return { + id: c.id, + title: c.name, + subtitle: `${kindLabel(c.kind, ui)} · ${scheduleText(c)}`, + // ⟲ N: how many skills the spec's RUNS names. Absent when none. + badge: spec && spec.runs.length > 0 ? `⟲ ${spec.runs.length}` : undefined, + badgeColor: spec && spec.runsResolved.some((r) => !r.ok) ? C.bad : undefined, + health: c.health, + tags: tagsOf(c), + haystack: `${c.id} ${c.name} ${c.what}`.toLowerCase(), + } + }), + [filtered, scheduleText, ui, specById, tagsOf], ) const runs = useMemo(() => { @@ -417,6 +449,21 @@ export default function CronExplorer() { + {/* The spec comes first: it is the statement; the boxes below + are what the code scan saw. */} + ({ + id: r.id, + ok: r.ok, + href: r.ok ? skillExplorerHash(r.id, { embedded }) : '#', + }))} + /> +
{ui.schedule}
{scheduleText(selected)}
@@ -484,7 +531,7 @@ export default function CronExplorer() {
)}
- {selected.tags.map((t) => ( + {tagsOf(selected).map((t) => (
- {selected.tags.map((t) => ( + {tagsOf(selected).map((t) => (