[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6K1WEC5F0382NJFSJHZGR91":3,"skill-same:01M6K1WEC5F0382NJFSJHZGR91":66},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":47,"ownerHtmlUrl":48,"ownerBlogUrl":49,"ownerDescription":50,"updatedAt":51,"install":52,"resources":65},"01M6K1WEC5F0382NJFSJHZGR91","scheduler",null,"Maintain Gnosi's backend scheduler lifecycle, persistence and task dispatch. Use for scheduler startup or task-state regressions, not private development-agent orchestration.","ismigar\u002FGnosi","ismigar","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6537880?v=4","01M6K1KJFJXCT6S4ATNXFRD1Y7","Ismael García","01M6K1KJM0CK16BDEYVZCV6949",1,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Fscheduler\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[5,24,25,26,27,28,29,30,31,32,33,34,35,36],"orchestration","development","state","persistence","startup","lifecycle","use","backend","dispatch","agent orchestration","task","maintain","for","2026-11-10 10:12:30",false,"---\nname: scheduler\ndescription: Maintain Gnosi's backend scheduler lifecycle, persistence and task dispatch. Use for scheduler startup or task-state regressions, not private development-agent orchestration.\n---\n\n# SKILL: Backend Background Scheduler\n\nThis skill defines the technical protocol for managing the Gnosi background task orchestrator (`SchedulerManager`).\n\n> ID: SCHEDULER-CORE-20260408\n> Core Logic: `backend\u002Fscheduler\u002Fmanager.py`\n> Status: ACTIVE\n\n---\n\n## 1. Objectives and Scope\n- **Main Objective**: Maintain a stable and efficient background loop for orchestrating Reader flows and maintenance tasks.\n- **Success Criteria**:\n    - Tasks execute at their defined intervals.\n    - Errors in one task do not stop the main scheduler loop.\n    - Task state (last run, results) is persisted correctly.\n\n---\n\n## 2. Core Principles\n\n### A. Background Loop\nConstruction loads configuration but does not start the thread. The application\nlifespan calls `start()` unless `GNOSI_DISABLE_SCHEDULER` disables it. The daemon\nloop checks enabled tasks every 60 seconds using `last_run` and\n`interval_minutes`; do not describe a separate persisted `next_run` timer.\n\n### B. Non-blocking Execution\nThe scheduler loop is outside the HTTP event loop. `run_task_now()` executes its\nhandler synchronously in its caller; individual domain jobs may start their own\nworkers. Do not assume every task gets another thread or promise concurrent\nexecution from this manager. Preserve domain-specific overlap and job policies.\nThe automatic loop waits 30 seconds by default before dispatching overdue work,\nso the HTTP server and active vault can become responsive first. Override this\nwith `GNOSI_SCHEDULER_STARTUP_DELAY_SECONDS`; setting it to zero is appropriate\nfor deterministic tests. Manual task submissions do not inherit this delay.\nThe lifespan itself schedules external integrations only after yielding startup,\nwith a five-second grace period controlled by\n`GNOSI_INTEGRATION_STARTUP_DELAY_SECONDS`. IMAP connection attempts therefore\ncannot keep the HTTP port closed.\n\n### C. State Persistence\nThe configured `SCHEDULER` path holds task definitions and lifecycle metadata.\nA per-device recovery mirror lives at\n`GNOSI_DATA_DIR\u002Fsystem\u002Fscheduler_config.local.json`; the manager resolves it\nthrough the canonical `LOCAL_DATA` path mapping. Execution history uses the\nmanagement database. Preserve both configuration sources when investigating a\ncloud-placeholder read failure; do not replace an unreadable file with defaults.\n\n---\n\n## 3. Operations and CLI\n\n### Manual Trigger\nTasks can be triggered manually via the REST API, bypassing the scheduled wait:\n```bash\n# A Personal Access Token is required once the backend runs with\n# GNOSI_REQUIRE_AUTH (create one in Settings). Without enforcement it is optional.\ncurl -X POST http:\u002F\u002Flocalhost:5002\u002Fapi\u002Fschedulers\u002F{task_name}\u002Frun \\\n  -H \"Authorization: Bearer $GNOSI_API_TOKEN\"\n```\n\n---\n\n## 4. Restrictions and Edge Cases\n\n- **Daemon State**: Keep startup\u002Fshutdown in the app lifespan. Unit tests must not start the scheduler or fire overdue integrations.\n- **Config Integrity**: Preserve configured paths and the recovery mirror. Missing tasks may be merged from defaults; unreadable configuration is not evidence that persisted user settings were empty.\n- **Path Sensitivity**: Background tasks must use absolute paths (via `paths_config.py`) to avoid resolution errors when running as a service.\n- **Host lock**: `start()` uses a local POSIX file lock when available; it is not a distributed multi-host lock. Preserve the non-POSIX fallback behavior.\n- **Verification**: Run `backend\u002Ftests\u002Ftest_scheduler_maintenance_scope.py`; its isolated child also selects the task-handler domain contracts before any backend configuration import. Use synthetic scheduler API and lifespan fixtures. Manual API execution is a real operation, not a documentation check.\n- **Startup responsiveness**: Do not dispatch overdue integrations in the same instant that the application lifespan is opening the HTTP service. Keep the bounded startup grace period interruptible through the scheduler stop event; never implement it as an unconditional sleep.\n\n---\n\n## 5. Learning Cycle (Live Memory)\n\n| Date | Error \u002F Learning | Root Cause | Solution \u002F Refinement |\n| --- | --- | --- | --- |\n| 2026-04-07 | Idle Scheduler | Missing loop implementation | Refactored `SchedulerManager` to include an active `while` loop and thread spawning. |\n| 2026-04-08 | Doc Displacement | Fragmentation | Moved directive from local `docs\u002F` to consolidated `Skill`. |\n| 2026-09-04 | Web unavailable during background catch-up | Enabled RSS, mail and maintenance tasks all dispatched on the scheduler's first cycle and competed with HTTP startup | Added an interruptible 30-second automatic-dispatch grace period while preserving immediate manual runs. |\n| 2026-09-04 | A scheduler test replaced `threading.Event` with a narrower recorder and failed strict typing | The production attribute is intentionally a concrete synchronization primitive | Test the pure delay policy directly and retain the real event for lifecycle behavior. |\n| 2026-09-04 | The scheduler grace expired before Uvicorn opened its port | Scheduler and IMAP startup preceded cache warmup and the lifespan yield | Moved external integration startup to a tracked post-yield task and cancel it during shutdown. |\n| 2026-09-04 | A post-yield lifecycle test expected a zero-delay task after one event-loop turn | `asyncio.sleep(0)` itself yields once before the deferred body continues | Give the tracked task two explicit loop turns in the isolated ordering contract. |\n","# SKILL: Backend Background Scheduler\n\nThis skill defines the technical protocol for managing the Gnosi background task orchestrator (`SchedulerManager`).\n\n> ID: SCHEDULER-CORE-20260408\n> Core Logic: `backend\u002Fscheduler\u002Fmanager.py`\n> Status: ACTIVE\n\n---\n\n## 1. Objectives and Scope\n- **Main Objective**: Maintain a stable and efficient background loop for orchestrating Reader flows and maintenance tasks.\n- **Success Criteria**:\n    - Tasks execute at their defined intervals.\n    - Errors in one task do not stop the main scheduler loop.\n    - Task state (last run, results) is persisted correctly.\n\n---\n\n## 2. Core Principles\n\n### A. Background Loop\nConstruction loads configuration but does not start the thread. The application\nlifespan calls `start()` unless `GNOSI_DISABLE_SCHEDULER` disables it. The daemon\nloop checks enabled tasks every 60 seconds using `last_run` and\n`interval_minutes`; do not describe a separate persisted `next_run` timer.\n\n### B. Non-blocking Execution\nThe scheduler loop is outside the HTTP event loop. `run_task_now()` executes its\nhandler synchronously in its caller; individual domain jobs may start their own\nworkers. Do not assume every task gets another thread or promise concurrent\nexecution from this manager. Preserve domain-specific overlap and job policies.\nThe automatic loop waits 30 seconds by default before dispatching overdue work,\nso the HTTP server and active vault can become responsive first. Override this\nwith `GNOSI_SCHEDULER_STARTUP_DELAY_SECONDS`; setting it to zero is appropriate\nfor deterministic tests. Manual task submissions do not inherit this delay.\nThe lifespan itself schedules external integrations only after yielding startup,\nwith a five-second grace period controlled by\n`GNOSI_INTEGRATION_STARTUP_DELAY_SECONDS`. IMAP connection attempts therefore\ncannot keep the HTTP port closed.\n\n### C. State Persistence\nThe configured `SCHEDULER` path holds task definitions and lifecycle metadata.\nA per-device recovery mirror lives at\n`GNOSI_DATA_DIR\u002Fsystem\u002Fscheduler_config.local.json`; the manager resolves it\nthrough the canonical `LOCAL_DATA` path mapping. Execution history uses the\nmanagement database. Preserve both configuration sources when investigating a\ncloud-placeholder read failure; do not replace an unreadable file with defaults.\n\n---\n\n## 3. Operations and CLI\n\n### Manual Trigger\nTasks can be triggered manually via the REST API, bypassing the scheduled wait:\n```bash\n# A Personal Access Token is required once the backend runs with\n# GNOSI_REQUIRE_AUTH (create one in Settings). Without enforcement it is optional.\ncurl -X POST http:\u002F\u002Flocalhost:5002\u002Fapi\u002Fschedulers\u002F{task_name}\u002Frun \\\n  -H \"Authorization: Bearer $GNOSI_API_TOKEN\"\n```\n\n---\n\n## 4. Restrictions and Edge Cases\n\n- **Daemon State**: Keep startup\u002Fshutdown in the app lifespan. Unit tests must not start the scheduler or fire overdue integrations.\n- **Config Integrity**: Preserve configured paths and the recovery mirror. Missing tasks may be merged from defaults; unreadable configuration is not evidence that persisted user settings were empty.\n- **Path Sensitivity**: Background tasks must use absolute paths (via `paths_config.py`) to avoid resolution errors when running as a service.\n- **Host lock**: `start()` uses a local POSIX file lock when available; it is not a distributed multi-host lock. Preserve the non-POSIX fallback behavior.\n- **Verification**: Run `backend\u002Ftests\u002Ftest_scheduler_maintenance_scope.py`; its isolated child also selects the task-handler domain contracts before any backend configuration import. Use synthetic scheduler API and lifespan fixtures. Manual API execution is a real operation, not a documentation check.\n- **Startup responsiveness**: Do not dispatch overdue integrations in the same instant that the application lifespan is opening the HTTP service. Keep the bounded startup grace period interruptible through the scheduler stop event; never implement it as an unconditional sleep.\n\n---\n\n## 5. Learning Cycle (Live Memory)\n\n| Date | Error \u002F Learning | Root Cause | Solution \u002F Refinement |\n| --- | --- | --- | --- |\n| 2026-04-07 | Idle Scheduler | Missing loop implementation | Refactored `SchedulerManager` to include an active `while` loop and thread spawning. |\n| 2026-04-08 | Doc Displacement | Fragmentation | Moved directive from local `docs\u002F` to consolidated `Skill`. |\n| 2026-09-04 | Web unavailable during background catch-up | Enabled RSS, mail and maintenance tasks all dispatched on the scheduler's first cycle and competed with HTTP startup | Added an interruptible 30-second automatic-dispatch grace period while preserving immediate manual runs. |\n| 2026-09-04 | A scheduler test replaced `threading.Event` with a narrower recorder and failed strict typing | The production attribute is intentionally a concrete synchronization primitive | Test the pure delay policy directly and retain the real event for lifecycle behavior. |\n| 2026-09-04 | The scheduler grace expired before Uvicorn opened its port | Scheduler and IMAP startup preceded cache warmup and the lifespan yield | Moved external integration startup to a tracked post-yield task and cancel it during shutdown. |\n| 2026-09-04 | A post-yield lifecycle test expected a zero-delay task after one event-loop turn | `asyncio.sleep(0)` itself yields once before the deferred body continues | Give the tracked task two explicit loop turns in the isolated ordering contract. |\n",{"name":5,"description":7},"main","ddf5fc580ae6656e98e3222889a9c49181664a45","pipeline\u002Fskills\u002Fscheduler",0,"https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi","https:\u002F\u002Fgnosi.temenosismael.org","https:\u002F\u002Fgithub.com\u002Fismigar","https:\u002F\u002Ftemenosismael.org","💻 & 📚 Computer scientist & philosopher. Exploring the intersection of tech & philosophy for social change & the common good.","2026-11-12 02:41:20",{"source":8,"skillName":5,"commands":53,"prompt":63,"zipAvailable":64},[54,57,60],{"tool":55,"command":56},"npx","npx skills add ismigar\u002FGnosi --skill scheduler",{"tool":58,"command":59},"bunx","bunx skills add ismigar\u002FGnosi --skill scheduler",{"tool":61,"command":62},"pnpm","pnpm dlx skills add ismigar\u002FGnosi --skill scheduler","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Fscheduler\u002FSKILL.md",true,[],[67,87,108,130,146,162],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":37},"01M6K1XMNQTR2Z9F0HV40M9CV6","zotero-sync-compatibility","Preserve the historical Zotero reference-table configuration path after Gnosi replaced bidirectional Zotero synchronization with its native reference manager.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Fzotero_sync\u002FSKILL.md",[],[74,75,76,77,78,79,80,81,82,83,84,85,86],"sync","Zotero","compatibility","zotero sync compatibility","preserve","path","manager","with","native","configuration","reference","synchronization","table",{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":37},"01M6K1XE33SSY5RXR5D4S8Y4RV","zotero-schema","Generate Gnosi's Python and TypeScript item-type constants from the repository-pinned Zotero schema, or perform an explicitly requested pinned schema refresh. Use for mapping or generated-constant maintenance, not runtime downloads.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Fzotero_schema\u002FSKILL.md",[],[75,94,95,96,97,98,99,100,36,101,102,103,104,30,105,106,107],"schema","zotero schema","generate","python","typescript","constants","repository","mapping","type","runtime","maintenance","generated","refresh","downloads",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":114,"publishedAt":37},"01M6K1X9M6AKK8JTQJA9G37ZRP","vault-core","Preserve Gnosi vault identifiers, internal links and active-vault context during file or metadata changes. Use for vault integrity work, not blanket normalization or cleanup of user documents.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Fvault_core\u002FSKILL.md",[],[115,116,117,118,78,119,36,120,121,30,122,123,124,125,126,127,128,129],"vault core","core","vault","integrity","changes","links","active","user","context","work","normalization","metadata","cleanup","documents","file",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":136,"publishedAt":37},"01M6K1X4TE0YP62D789CJWZ4YY","translate-row","Translate configured table-row fields and Markdown into language children, updating existing translations in place. Use for the explicit translate-row action or maintenance of its provider adapters.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Ftranslate_row\u002FSKILL.md",[],[137,138,139,104,140,36,141,142,121,143,144,30,86,145],"translate","translate row","fields","Existing","action","Adapters","language","provider","markdown",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":155,"publishedAt":37},"01M6K1X0G5C2E91RS71NMX0GHG","translate-page","Translate a Vault page title and Markdown body into language children, updating existing translations and preserving Gnosi directives. Use for the explicit translate-page action or Markdown segmentation maintenance.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Ftranslate_page\u002FSKILL.md",[152],{"categoryNo":153,"categoryName":154,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[156,137,157,104,30,145,117,158,143,159,160,161,140,36,141,121],"page","translate page","segmentation","body","title","directives",{"skillNo":163,"name":164,"nameZh":6,"description":165,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":166,"categories":167,"keywords":168,"publishedAt":37},"01M6K1WSRJTN93ZY4VPNZ2BJW3","technical-documentation","Generate and validate Gnosi's engineering portal from reviewed guides and deterministic source catalogs. Use for source-traceable documentation updates and pre-PR gates; never translate generated catalogs with a model.","https:\u002F\u002Fgithub.com\u002Fismigar\u002FGnosi\u002Fblob\u002Fmain\u002Fpipeline\u002Fskills\u002Ftechnical_documentation\u002FSKILL.md",[],[169,170,171,172,173,36,174,81,175,176,177,30,105,178,179,96,180,181,137],"technical","technical documentation","documentation","gates","source","updates","guides","deterministic","never","model","engineering","pr","validate"]