From fb8f45e9faa1698f619fd8c18c46eb0264f4d695 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Mon, 11 May 2026 20:07:08 +0200 Subject: [PATCH] .agents: adopt agentsfolder/spec v0.1 as canonical agent configuration Per template (singularity-forge f3d84cd11). Project-specific manifest + prompts/project.md capture: post-rename namespace com.centralcloud.oncall (io.heckel.ntfy fully gone), centrally-configured-from-server design, strict-opt-in SMS relay, greenfield (no backwards compat with upstream ntfy). Legacy docs (AGENTS.md, README.md, TESTING.md) kept; .agents/ canonical going forward. Co-Authored-By: Claude Opus 4.7 (1M context) --- .agents/manifest.yaml | 53 +++++++++++++++++++ .agents/modes/ask.md | 36 +++++++++++++ .agents/modes/build.md | 40 ++++++++++++++ .agents/policies/default-safe.yaml | 50 +++++++++++++++++ .agents/policies/yolo.yaml | 44 +++++++++++++++ .agents/profiles/.gitkeep | 3 ++ .agents/prompts/base.md | 12 +++++ .agents/prompts/project.md | 49 +++++++++++++++++ .agents/prompts/snippets/.gitkeep | 1 + .agents/schemas/.gitkeep | 3 ++ .agents/scopes/.gitkeep | 3 ++ .agents/skills/.gitkeep | 2 + .../skills/forge-autonomous-runtime/SKILL.md | 37 +++++++++++++ .agents/skills/forge-command-surface/SKILL.md | 48 +++++++++++++++++ .agents/skills/nix-build/SKILL.md | 26 +++++++++ .agents/skills/smoke-test/SKILL.md | 17 ++++++ .agents/state/.gitignore | 3 ++ 17 files changed, 427 insertions(+) create mode 100644 .agents/manifest.yaml create mode 100644 .agents/modes/ask.md create mode 100644 .agents/modes/build.md create mode 100644 .agents/policies/default-safe.yaml create mode 100644 .agents/policies/yolo.yaml create mode 100644 .agents/profiles/.gitkeep create mode 100644 .agents/prompts/base.md create mode 100644 .agents/prompts/project.md create mode 100644 .agents/prompts/snippets/.gitkeep create mode 100644 .agents/schemas/.gitkeep create mode 100644 .agents/scopes/.gitkeep create mode 100644 .agents/skills/.gitkeep create mode 100644 .agents/skills/forge-autonomous-runtime/SKILL.md create mode 100644 .agents/skills/forge-command-surface/SKILL.md create mode 100644 .agents/skills/nix-build/SKILL.md create mode 100644 .agents/skills/smoke-test/SKILL.md create mode 100644 .agents/state/.gitignore diff --git a/.agents/manifest.yaml b/.agents/manifest.yaml new file mode 100644 index 00000000..ed2f090a --- /dev/null +++ b/.agents/manifest.yaml @@ -0,0 +1,53 @@ +# .agents/ canonical agent configuration +# Spec: https://github.com/agentsfolder/spec +# +# Status: pre-1.0 spec adoption. Schema may shift. + +specVersion: "0.1.0" + +defaults: + mode: build + policy: default-safe + +enabled: + modes: + - ask + - build + policies: + - default-safe + - yolo + skills: [] + +resolution: + enableUserOverlay: false + denyOverridesAllow: true + onConflict: error + +project: + name: oncall-mobile-android + description: >- + Centralcloud OnCall — Android pager app. Forked from ntfy-android, + fully renamed (package com.centralcloud.oncall). Includes the + SMS-relay subsystem that forwards whitelisted incoming SMS to + centralcloud-ops via /api/sms/inbound/phone-relay. + languages: + - kotlin + - java + frameworks: + - android + - okhttp + - workmanager + - room + +x: + centralcloud: + legacy_pointers: + - AGENTS.md + - README.md + - TESTING.md + - flake.nix + note: >- + Package namespace post-rename is com.centralcloud.oncall. The + io.heckel.ntfy namespace is entirely removed from the source + tree as of commit d5694a32. Custom subsystems live under + com.centralcloud.oncall.sms (SMS relay + device heartbeat). diff --git a/.agents/modes/ask.md b/.agents/modes/ask.md new file mode 100644 index 00000000..e0b9584f --- /dev/null +++ b/.agents/modes/ask.md @@ -0,0 +1,36 @@ +--- +id: ask +title: Ask +policy: default-safe +enableSkills: [] +disableSkills: [] +includeSnippets: [] +toolIntent: + allow: + - read + - search + - web_fetch + deny: + - write + - exec_command + - git_commit + - git_push +--- + +# Ask Mode + +Read-only investigation. Answer questions about the codebase, propose +plans, but **make no changes**. + +Use this mode when: + +- The user is exploring or trying to understand something. +- A plan needs review before implementation. +- The right next step is unclear and probing the code base will help. + +Switch to Build (Shift+Tab) when the user is ready for the agent to +make changes. + +This mode's policy denies writes, command execution, and git mutation. +If a task requires any of those, surface that fact rather than +attempting and failing. diff --git a/.agents/modes/build.md b/.agents/modes/build.md new file mode 100644 index 00000000..f1edfb69 --- /dev/null +++ b/.agents/modes/build.md @@ -0,0 +1,40 @@ +--- +id: build +title: Build +policy: default-safe +enableSkills: [] +disableSkills: [] +includeSnippets: [] +toolIntent: + allow: + - read + - search + - web_fetch + - write + - exec_command + - git_commit + deny: + - git_push_force + - rm_rf + - drop_table +--- + +# Build Mode + +Active development. Make changes, run tests, commit. + +Confirmations are required for destructive operations per the +default-safe policy (rm -rf, git push --force, git reset --hard, +drop_table, etc.). Toggle the YOLO flag (Ctrl+Y) to drop those +confirmations — that's a flag on Build, not a separate mode. + +In this mode the agent is expected to: + +- Run tests before committing. +- Group related changes in a single commit; don't bundle unrelated + work. +- Use Git history as the rationale store — clear commit messages, + rationale in the body, link to ADRs when relevant. +- Update tests when changing behaviour. +- Promote durable decisions to `docs/adr/` rather than burying them + in commit messages. diff --git a/.agents/policies/default-safe.yaml b/.agents/policies/default-safe.yaml new file mode 100644 index 00000000..1d2d5a5e --- /dev/null +++ b/.agents/policies/default-safe.yaml @@ -0,0 +1,50 @@ +id: default-safe +description: >- + Conservative default. Confirmations required for destructive + filesystem and git operations; network and exec allowed but logged. + +capabilities: + filesystem: + read: allow + write: confirm + delete: confirm + exec: + enabled: confirm + network: + enabled: allow + allow_hosts: + - "*" + deny_hosts: [] + mcp: + enabled: allow + +paths: + allow: + - "**" + deny: + - "~/.ssh/**" + - "**/.env" + - "**/.env.*" + - "**/secrets/**" + - ".sf/sf.db" + - ".sf/sf.db-*" + - ".sf/backups/**" + redact: + - "**/*api_key*" + - "**/*token*" + - "**/*password*" + - "**/.env*" + +confirmations: + requiredFor: + - rm -rf + - git push --force + - git push -f + - git reset --hard + - git clean -fdx + - drop_table + - drop_database + +limits: + max_files_per_op: 100 + max_command_runtime_sec: 600 diff --git a/.agents/policies/yolo.yaml b/.agents/policies/yolo.yaml new file mode 100644 index 00000000..de42e80d --- /dev/null +++ b/.agents/policies/yolo.yaml @@ -0,0 +1,44 @@ +id: yolo +description: >- + Confirmation-free build mode. Use deliberately — destructive + operations execute without prompting. Still respects path denies + and redactions; only the confirmation gate is removed. + +capabilities: + filesystem: + read: allow + write: allow + delete: allow + exec: + enabled: allow + network: + enabled: allow + allow_hosts: + - "*" + deny_hosts: [] + mcp: + enabled: allow + +paths: + allow: + - "**" + deny: + - "~/.ssh/**" + - "**/.env" + - "**/.env.*" + - "**/secrets/**" + - ".sf/sf.db" + - ".sf/sf.db-*" + - ".sf/backups/**" + redact: + - "**/*api_key*" + - "**/*token*" + - "**/*password*" + - "**/.env*" + +confirmations: + requiredFor: [] + +limits: + max_files_per_op: 1000 + max_command_runtime_sec: 3600 diff --git a/.agents/profiles/.gitkeep b/.agents/profiles/.gitkeep new file mode 100644 index 00000000..759f0cd4 --- /dev/null +++ b/.agents/profiles/.gitkeep @@ -0,0 +1,3 @@ +# profiles/ is REQUIRED per .agents spec but MAY be empty. +# Profiles are named overlays (e.g., "dev", "ci") that modify +# canonical configuration. None defined yet. diff --git a/.agents/prompts/base.md b/.agents/prompts/base.md new file mode 100644 index 00000000..109f1f27 --- /dev/null +++ b/.agents/prompts/base.md @@ -0,0 +1,12 @@ +# Base Prompt + +You are an AI agent working in this repository. Before changing code: + +1. Read the file you're editing in full. +2. Read related files (callers, callees, tests). +3. Match existing patterns and style. +4. Add or update tests for behavior changes. + +Default to the smallest change that solves the problem. Prefer fixing +the root cause over patching the symptom. Surface uncertainties to the +operator rather than guessing. diff --git a/.agents/prompts/project.md b/.agents/prompts/project.md new file mode 100644 index 00000000..d7aca256 --- /dev/null +++ b/.agents/prompts/project.md @@ -0,0 +1,49 @@ +# Project Prompt — oncall-mobile-android + +## What this is + +Centralcloud OnCall Android pager. Forked from `binwiederhier/ntfy-android`; +fully renamed to `com.centralcloud.oncall` (no `io.heckel.ntfy` left in +the source tree). + +Engineer's single pane for on-call: paging, conference bridge, agent +chat, plus an SMS-relay subsystem that forwards whitelisted incoming +SMS from this device's number to centralcloud-ops. + +For the long form see [`AGENTS.md`](../../AGENTS.md). For test setup +see [`TESTING.md`](../../TESTING.md). For the build shell see +[`flake.nix`](../../flake.nix). + +## Non-negotiables + +- **Package namespace is `com.centralcloud.oncall`.** Don't reintroduce + `io.heckel.ntfy` in any file — the rename is complete and absolute. +- **Centrally configured.** App hits + `GET https://ops.centralcloud.com/api/android/config` on first + launch — everything else (server URLs, default topics, FCM project) + comes from there. Hardcode at most one URL. +- **Strict opt-in for SMS relay.** `SmsRelayPreferences.enabled` + defaults false. Whitelist is required (empty set → forward nothing). + Disclosure must be clear in the settings UI — the app reads + incoming SMS, and that's sensitive. +- **Greenfield, no backwards compat with upstream ntfy.** Backup + format magic was rewritten (`oncall26`), URL scheme is `oncall://`, + intent action namespaces are `com.centralcloud.oncall.*` (e.g. + `com.centralcloud.oncall.SEND_MESSAGE`). + +## Workflow + +- Build via Nix dev shell: `nix develop` then `./gradlew assembleDebug`. +- Tests: see TESTING.md. +- Install on ops phones: `adb install -r app/build/outputs/apk/...`. +- SMS-relay config: launch + `adb shell am start -n com.centralcloud.oncall/.sms.SmsRelaySettingsActivity`, + set base URL + device ID + whitelist + toggle on. + +## Sibling repos + +- **`centralcloud/infra`** — the centralcloud-ops Phoenix app this + connects to (`/api/push/*`, `/api/sms/inbound/phone-relay`, + `/api/devices/heartbeat`, etc.). +- **`centralcloud/operations-memory`** — Go memory service. +- **`singularity/singularity-forge`** — SF planning tool. diff --git a/.agents/prompts/snippets/.gitkeep b/.agents/prompts/snippets/.gitkeep new file mode 100644 index 00000000..1476e469 --- /dev/null +++ b/.agents/prompts/snippets/.gitkeep @@ -0,0 +1 @@ +# Snippets composed into modes via Mode front matter `includeSnippets`. diff --git a/.agents/schemas/.gitkeep b/.agents/schemas/.gitkeep new file mode 100644 index 00000000..d4d1efcb --- /dev/null +++ b/.agents/schemas/.gitkeep @@ -0,0 +1,3 @@ +# schemas/ is REQUIRED per .agents spec but MAY be generated. +# Tooling that validates .agents/ configuration writes JSON Schema +# files here. Treat as generated output, not hand-edited. diff --git a/.agents/scopes/.gitkeep b/.agents/scopes/.gitkeep new file mode 100644 index 00000000..faf80b72 --- /dev/null +++ b/.agents/scopes/.gitkeep @@ -0,0 +1,3 @@ +# scopes/ is REQUIRED per .agents spec but MAY be empty. +# Scopes provide path-based overrides for monorepos. SF is a single +# tree today; add scopes if/when subprojects need different policies. diff --git a/.agents/skills/.gitkeep b/.agents/skills/.gitkeep new file mode 100644 index 00000000..c97906d5 --- /dev/null +++ b/.agents/skills/.gitkeep @@ -0,0 +1,2 @@ +# skills/ is REQUIRED per .agents spec but MAY be empty. +# Skills declared here MUST follow https://agentskills.io/specification. diff --git a/.agents/skills/forge-autonomous-runtime/SKILL.md b/.agents/skills/forge-autonomous-runtime/SKILL.md new file mode 100644 index 00000000..40c0f005 --- /dev/null +++ b/.agents/skills/forge-autonomous-runtime/SKILL.md @@ -0,0 +1,37 @@ +--- +name: forge-autonomous-runtime +description: Explains SF autonomous loop, UOK gates, installed-runtime drift, and recovery paths. +user-invocable: false +model-invocable: true +side-effects: none +permission-profile: restricted +triggers: + - "*" +--- + +# forge-autonomous-runtime + +## Context + +SF's autonomous mode is governed by the Unified Operation Kernel (UOK): + +1. **State reading** — UOK reads canonical project state from `.sf/sf.db` +2. **Ledger recording** — Each run is recorded in the DB-backed ledger +3. **Projection** — Runtime files under `.sf/runtime/` are generated projections +4. **Dispatch** — UOK determines the next unit of work and creates a fresh agent session +5. **Execution** — LLM executes with focused prompt and pre-inlined context +6. **Reconciliation** — UOK reconciles ledger and projections before next dispatch + +## Recovery Paths + +- **Crash recovery** — Lock file tracks current unit; next `/autonomous` reads surviving state +- **Stuck detection** — Loop detects stuck iterations and emits `stuck-detected` journal events +- **Health gates** — Pre-dispatch gates check state integrity before execution +- **Parity reports** — Ledger parity ensures no orphaned or missing unit records + +## Rules + +- Never modify `.sf/sf.db` directly — use UOK APIs +- Never trust `.sf/runtime/` files as authoritative — they are projections +- Always check `runControl` and `permissionProfile` before tool invocation +- Journal events are best-effort; absence is a failure signal diff --git a/.agents/skills/forge-command-surface/SKILL.md b/.agents/skills/forge-command-surface/SKILL.md new file mode 100644 index 00000000..0d7654a8 --- /dev/null +++ b/.agents/skills/forge-command-surface/SKILL.md @@ -0,0 +1,48 @@ +--- +name: forge-command-surface +description: Use when changing SF slash commands, browser command parity, or headless command dispatch. +user-invocable: true +model-invocable: true +side-effects: code-edits +permission-profile: normal +triggers: + - build + - code + - "*" +--- + +# forge-command-surface + +## When to Use + +This skill applies when: +- Adding or modifying SF slash commands (`/mode`, `/tasks`, `/skills`, etc.) +- Changing command handlers in `src/resources/extensions/sf/commands/handlers/` +- Updating command catalog descriptions +- Ensuring web/TUI/headless command parity +- Modifying command dispatch routing + +## Instructions + +1. **Check existing handlers** — Look in `commands/handlers/core.js` and `commands/handlers/ops.js` for the pattern +2. **Update catalog** — Add to `commands/catalog.js` `TOP_LEVEL_SUBCOMMANDS` +3. **Update help text** — Add to `showHelp()` in `commands/handlers/core.js` +4. **Wire dispatch** — Add routing in `handleCoreCommand()` or `handleOpsCommand()` +5. **Test** — Verify with `node --check` and manual test + +## Verification + +- [ ] Command appears in `/help` +- [ ] Command appears in `/help all` +- [ ] Handler file passes `node --check` +- [ ] No `/sf` prefix in user-facing strings + +## Examples + +```javascript +// Adding a new command +if (trimmed === "mycommand" || trimmed.startsWith("mycommand ")) { + await handleMyCommand(trimmed.replace(/^mycommand\s*/, "").trim(), ctx); + return true; +} +``` diff --git a/.agents/skills/nix-build/SKILL.md b/.agents/skills/nix-build/SKILL.md new file mode 100644 index 00000000..bb97901a --- /dev/null +++ b/.agents/skills/nix-build/SKILL.md @@ -0,0 +1,26 @@ +--- +name: nix-build +description: Build any @singularity-forge/* package (or the full stack) via nix develop. Pass a package name like "pi-coding-agent", "native", "daemon", or "all" for a full core build. +--- + +All build commands in this repo must run inside `nix develop`. Never use bare cargo/rustc. + +For a single package: +``` +nix develop --command bash -c "npm run --workspace=@singularity-forge/ build" +``` + +For the full core build (native + all TS packages): +``` +nix develop --command bash -c "npm run build:core" +``` + +For typecheck only: +``` +nix develop --command bash -c "tsc --noEmit -p tsconfig.json" +``` + +For extensions typecheck: +``` +nix develop --command bash -c "tsc --noEmit -p tsconfig.extensions.json" +``` diff --git a/.agents/skills/smoke-test/SKILL.md b/.agents/skills/smoke-test/SKILL.md new file mode 100644 index 00000000..dc2c016c --- /dev/null +++ b/.agents/skills/smoke-test/SKILL.md @@ -0,0 +1,17 @@ +--- +name: smoke-test +description: Run the standard sf-run smoke tests (--version, --help, --print). All three must pass before shipping a build. +--- + +#!/bin/bash +set -e +echo "=== --version ===" +node dist/loader.js --version + +echo "=== --help (first 5 lines) ===" +node dist/loader.js --help 2>&1 | head -5 + +echo "=== --print (graceful degradation) ===" +node dist/loader.js --print 2>&1 | head -5 + +echo "All smoke tests passed." diff --git a/.agents/state/.gitignore b/.agents/state/.gitignore new file mode 100644 index 00000000..04928aed --- /dev/null +++ b/.agents/state/.gitignore @@ -0,0 +1,3 @@ +# Per .agents/ spec: state.yaml is per-developer convenience state +# (mode/profile/backend selection). Never commit. +state.yaml