.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) <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-05-11 20:07:08 +02:00
parent 78815896cc
commit fb8f45e9fa
17 changed files with 427 additions and 0 deletions

53
.agents/manifest.yaml Normal file
View file

@ -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).

36
.agents/modes/ask.md Normal file
View file

@ -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.

40
.agents/modes/build.md Normal file
View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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.

12
.agents/prompts/base.md Normal file
View file

@ -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.

View file

@ -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.

View file

@ -0,0 +1 @@
# Snippets composed into modes via Mode front matter `includeSnippets`.

3
.agents/schemas/.gitkeep Normal file
View file

@ -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.

3
.agents/scopes/.gitkeep Normal file
View file

@ -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.

2
.agents/skills/.gitkeep Normal file
View file

@ -0,0 +1,2 @@
# skills/ is REQUIRED per .agents spec but MAY be empty.
# Skills declared here MUST follow https://agentskills.io/specification.

View file

@ -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

View file

@ -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;
}
```

View file

@ -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/<package> 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"
```

View file

@ -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."

3
.agents/state/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Per .agents/ spec: state.yaml is per-developer convenience state
# (mode/profile/backend selection). Never commit.
state.yaml