arcane-cli 1.17.3 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/dist/assets/.arcane/governance/threat-model.md +43 -0
- package/dist/assets/.arcane/spells/_fragments/presence-report.md +16 -0
- package/dist/assets/.arcane/spells/spell-close-session.md +19 -0
- package/dist/assets/.arcane/spells/spell-commit-work.md +19 -0
- package/dist/assets/.arcane/spells/spell-implement.md +19 -0
- package/dist/assets/.arcane/spells/spell-open-session.md +19 -0
- package/dist/assets/schemas/presence.schema.json +586 -0
- package/dist/index.js +1681 -263
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -273,6 +273,77 @@ The document is derived, never authoritative, and deterministic: two runs differ
|
|
|
273
273
|
|
|
274
274
|
The shape is the JSON Schema at [`src/assets/schemas/state.schema.json`](src/assets/schemas/state.schema.json) (`$id` `https://arcaneai.net/schemas/state/v1`), shipped in the package as `dist/assets/schemas/state.schema.json`. Schema `1.0.0` is additive-only: a field is never removed or retyped; a breaking change is a new major version and a new decision record ([ARC-044](DECISIONS.md#arc-044--client-architecture-files-first-state-contract-and-a-local-presence-channel)).
|
|
275
275
|
|
|
276
|
+
### Presence
|
|
277
|
+
|
|
278
|
+
`spell presence` is an optional, local presence channel: which session is working on what, right now, and whether it is waiting for you ([ARC-060](DECISIONS.md#arc-060--the-presence-channel-one-package-behind-spell-presence-a-loopback-contract-checkout-keyed-sessions-and-a-prompt-driven-reporting-convention); working name `arcane-mcp`). You start the service; nothing listens until you do. It binds `127.0.0.1` only, keeps sessions in memory only (stale after 90 s without a report, dropped after 24 h), writes nothing but its token and `server.json` under `~/.arcane/presence/`, and never writes a repository file. It is observation only: everything it holds is reconstructable from `spell state --json`, and no route, tool or event steers a session. Spells report through it with one fragment in `spell-open-session`, `spell-close-session`, `spell-commit-work` and `spell-implement`; with no service running they behave exactly as before.
|
|
279
|
+
|
|
280
|
+
| Command | What it does |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| `spell presence serve [--port <n>] [--rotate]` | Runs the service in the foreground on `127.0.0.1` (port 27226, or `ARCANE_PRESENCE_PORT`) until Ctrl+C. `--rotate` replaces the token |
|
|
283
|
+
| `spell presence status` | Exit 0 when the service answers this machine's token, 1 otherwise |
|
|
284
|
+
| `spell presence stop` | Stops the recorded service; exit 0 either way |
|
|
285
|
+
| `spell presence report --status <s> [--task ...] [--needs-you ...]` | Reports this checkout's session; silent and exit 0 with no service, never blocks |
|
|
286
|
+
| `spell presence end` | Ends this checkout's session |
|
|
287
|
+
| `spell presence mcp` | The same channel as an MCP server over stdio: tools `presence_report` and `presence_end`, resources `arcane://presence/sessions` and `arcane://presence/capabilities` |
|
|
288
|
+
|
|
289
|
+
**The token** is `~/.arcane/presence/token` (`0600` in a `0700` directory; restricted to your account on Windows). A subscriber that cannot read the file, such as a dashboard's server, gets it from the `ARCANE_PRESENCE_TOKEN` environment variable instead. No command takes a token as an argument. **Remote access** is only ever a tunnel you put in front of the loopback port, with the token still required. The contract is HTTP plus server-sent events under `/v0/`; its JSON Schema is [`src/assets/schemas/presence.schema.json`](src/assets/schemas/presence.schema.json) (`$id` `https://arcaneai.net/schemas/presence/v0`), shipped as `dist/assets/schemas/presence.schema.json`.
|
|
290
|
+
|
|
291
|
+
**Registering the MCP server** is yours; Arcane writes no client's configuration. The server accepts a `cwd` argument on both tools, because a client may launch it from any directory.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
claude mcp add --transport stdio --scope user arcane-presence -- spell presence mcp # Claude Code
|
|
295
|
+
codex mcp add arcane-presence -- spell presence mcp # Codex CLI
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
For GitHub Copilot in VS Code, add it to `.vscode/mcp.json` (or your user `mcp.json`):
|
|
299
|
+
|
|
300
|
+
```json
|
|
301
|
+
{ "servers": { "arcane-presence": { "type": "stdio", "command": "spell", "args": ["presence", "mcp"] } } }
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Running it as a user service** is optional. Use absolute paths from `which node` and `which spell` (`where` on Windows), because services start without your shell's `PATH`.
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary>macOS (launchd), Linux (systemd --user), Windows (Task Scheduler)</summary>
|
|
308
|
+
|
|
309
|
+
macOS: save as `~/Library/LaunchAgents/net.arcaneai.presence.plist`, then `launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/net.arcaneai.presence.plist`.
|
|
310
|
+
|
|
311
|
+
```xml
|
|
312
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
313
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
314
|
+
<plist version="1.0">
|
|
315
|
+
<dict>
|
|
316
|
+
<key>Label</key><string>net.arcaneai.presence</string>
|
|
317
|
+
<key>ProgramArguments</key>
|
|
318
|
+
<array><string>/absolute/path/to/node</string><string>/absolute/path/to/spell</string><string>presence</string><string>serve</string></array>
|
|
319
|
+
<key>RunAtLoad</key><true/>
|
|
320
|
+
<key>KeepAlive</key><true/>
|
|
321
|
+
</dict>
|
|
322
|
+
</plist>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Linux: save as `~/.config/systemd/user/arcane-presence.service`, then `systemctl --user daemon-reload` and `systemctl --user enable --now arcane-presence.service`.
|
|
326
|
+
|
|
327
|
+
```ini
|
|
328
|
+
[Unit]
|
|
329
|
+
Description=Arcane presence service (loopback only)
|
|
330
|
+
|
|
331
|
+
[Service]
|
|
332
|
+
ExecStart=/absolute/path/to/node /absolute/path/to/spell presence serve
|
|
333
|
+
Restart=on-failure
|
|
334
|
+
|
|
335
|
+
[Install]
|
|
336
|
+
WantedBy=default.target
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Windows: a task that starts at logon. If Windows answers "Access is denied", create the same task from an elevated prompt or in Task Scheduler's window.
|
|
340
|
+
|
|
341
|
+
```bat
|
|
342
|
+
schtasks /create /sc onlogon /tn "Arcane presence" /tr "\"C:\path\to\spell.cmd\" presence serve"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
</details>
|
|
346
|
+
|
|
276
347
|
### Profiles
|
|
277
348
|
|
|
278
349
|
Install everything, or just the slice you want:
|
|
@@ -72,6 +72,49 @@ See also: [Hardening Checklist](hardening-checklist.md), [Authentication Strateg
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
+
## Local Presence Service (`spell presence`)
|
|
76
|
+
|
|
77
|
+
> **Status:** every row is **Planned** until the operator verifies it against the installed build on each machine that runs the service, and records the result in the security audit log. Decision record: ARC-060 (the presence channel) in the Arcane framework repository.
|
|
78
|
+
|
|
79
|
+
`spell presence serve` is an optional, operator-started foreground process that AI sessions report to (agent, task, repository, status and "needs you" lines) and that dashboards read. Nothing listens until the operator starts it. It adds one listening socket and one secret to the machine. It is **observation only by contract** (capability descriptor `{ observe: true, actions: [] }`) and **never authoritative**: everything it holds is reconstructable from `spell state --json` and the repository's files.
|
|
80
|
+
|
|
81
|
+
| Asset | Value | Location |
|
|
82
|
+
|-------|-------|----------|
|
|
83
|
+
| Presence state | Each session's agent, task, message, needs-you lines, repository name and remote, branch and checkout path. Business-private: it can name ventures, customers and machine paths | Process memory only; gone on restart, dropped after 24 h of silence |
|
|
84
|
+
| Presence token | Bearer capability for every route: read every live session, post or end any session | `~/.arcane/presence/token` (file `0600` in a `0700` directory; on Windows restricted to the current account) or the `ARCANE_PRESENCE_TOKEN` environment variable for a subscriber |
|
|
85
|
+
| `server.json` | URL, process id, port, start time and version of the running service. Not a secret | `~/.arcane/presence/server.json`, removed on a clean exit |
|
|
86
|
+
|
|
87
|
+
**Threat actors added:**
|
|
88
|
+
|
|
89
|
+
- **A local process without the token:** another account on the machine, or a web page in a browser reaching the loopback port, including through DNS rebinding.
|
|
90
|
+
- **A process running as the operator's own account:** a compromised AI session, or any agent runtime that runs under that account, can read the token file like the operator can. It can post forged presence and read every live session.
|
|
91
|
+
- **A tunnel or VPN peer**, only when the operator puts a tunnel in front of the loopback port.
|
|
92
|
+
|
|
93
|
+
| Surface | Status | Mitigation (ARC-060 decision) | How to verify |
|
|
94
|
+
|---------|--------|-------------------------------|---------------|
|
|
95
|
+
| Listening socket | **Planned** | Binds `127.0.0.1` only; no host or bind option exists (decision 2) | `lsof -nP -iTCP:<port> -sTCP:LISTEN` (macOS) or `ss -ltnp 'sport = :<port>'` (Linux) shows `127.0.0.1` only; a connection from another host to the machine's address on that port is refused; `spell presence serve --help` lists no host or bind option |
|
|
96
|
+
| Authentication | **Planned** | Every `/v0/` route requires `Authorization: Bearer <token>`; the only unauthenticated answer is `401` with an empty body; constant-time comparison (decisions 3, 6) | `curl -si http://127.0.0.1:<port>/v0/health` without the token and with a wrong one both answer `401` with no body; with the token, `200` |
|
|
97
|
+
| Token storage | **Planned** | 32 random bytes written on first `serve`, `0600` in `0700`; the write is refused when the restriction fails; `--rotate` replaces it; readers take `ARCANE_PRESENCE_TOKEN` first (decision 6). On Windows inherited entries are removed and the current account granted; SYSTEM and the Administrators group can keep explicit entries | `stat` on `~/.arcane/presence` and its `token` shows `drwx------` and `-rw-------`; on Windows `icacls` on the token shows no inherited entry and no principal other than the current account, SYSTEM and Administrators |
|
|
98
|
+
| Token exposure | **Planned** | No command takes a token as an argument; `server.json` holds none; no log line prints one (decision 6) | `spell presence --help` and every subcommand's help list no token option; a search of a `serve` run's output for the token value finds nothing |
|
|
99
|
+
| Browser and DNS-rebinding reach | **Planned** | No CORS headers, so a cross-origin page cannot read an answer, and the bearer header forces a preflight that is refused. There is no `Host` check (decision 3), so the token is the only control here | A `fetch` to `http://127.0.0.1:<port>/v0/sessions` from any web page fails; a request with a foreign `Host` header and no token answers `401` |
|
|
100
|
+
| Repository and filesystem writes | **Planned** | Writes only `token` and `server.json` under `~/.arcane/presence/`; never a repository file, never a session journal (decisions 2, 7) | A filesystem watch across a `serve` run with reports from two checkouts shows writes under `~/.arcane/presence/` only; `git status` is clean in both checkouts afterwards |
|
|
101
|
+
| State persistence | **Planned** | Memory only; a session silent for 90 s is stale, for 24 h expired and dropped; a restart starts empty (decision 7) | Restart `serve`: `GET /v0/sessions` answers an empty list; a session silent for 90 s shows `stale: true` |
|
|
102
|
+
| Forged or poisoned presence | **Accepted risk** | Any token holder can post or end any session. Bounded: presence is never authoritative and steers nothing (`actions: []`, decision 8); a forged report misleads a dashboard and changes no file, branch or decision | `tools/list` on `spell presence mcp` answers only `presence_report` and `presence_end`; the HTTP surface has six routes, none of which acts |
|
|
103
|
+
| Disclosure to token holders | **Accepted risk** | Every token holder can read every session's task, message and path. A process of the operator's account can already read those repositories, so this adds no new data to that account; it is why the token is never shared beyond it | Review which agent runtimes run under the operator's account and what they may read |
|
|
104
|
+
| Request limits | **Planned** | 64 KiB bodies, 256-character lines, at most 20 needs-you lines and 32 aliases (more is refused), 256 sessions, 32 subscribers, a subscriber that stops reading closed past a 1 MiB backlog, connection and header time limits checked every second (decision 3) | A 65 KiB body and a 21-line needs-you list are each refused; the service keeps answering |
|
|
105
|
+
| Execution and reads | **Planned** | The service runs no process and reads nothing but its own store; it makes no outbound request (decisions 9, 10) | Code review of the release; no outbound connection from the `serve` process during a run (`lsof -p <pid> -i` shows the listener only) |
|
|
106
|
+
| Logging | **Planned** | The service logs an event name, a session reference and a status only; never a task, message, needs-you line, path or token (decision 9) | `serve`'s output across a run contains no task, message, path or token |
|
|
107
|
+
| Remote access | **Planned** | Nothing listens beyond loopback; the only remote path is a tunnel or VPN the operator places in front of the loopback port, with the token still required (decision 6) | Before exposing the port through a tunnel, review which devices the tunnel admits and record them |
|
|
108
|
+
| Git hooks | **Planned** | The reporter is invoked by spell prompts, never by a git hook; `spell presence` is never installed into `.husky/` or `.arcane/hooks/` (decision 10) | A search for `presence` in `.husky/` and `.arcane/hooks/` of each repository after the update that ships it finds nothing |
|
|
109
|
+
| Data leaving the machine | **Planned** | No outbound request; a pushed projection would be a later decision record and strips `repository.root` and `checkout.path` first (decision 9) | Same as the execution row |
|
|
110
|
+
|
|
111
|
+
Two further surfaces, found while designing the service, with their mitigations:
|
|
112
|
+
|
|
113
|
+
- **A port taken by another process after a crash.** If the service dies without removing `server.json`, another local process could bind the port and receive the token from the next reporter. The reporter treats a `server.json` whose process id is not alive as no channel, and `serve` on a port in use fails without touching the token; a reused process id together with a taken port is the residual.
|
|
114
|
+
- **A rewritten `server.json`.** A process of the operator's account can edit it. The client takes only the port from it and always connects to `127.0.0.1`, so the token is never sent to another host; a recorded process id that belongs to another account counts as no service, so the token is not sent to whatever holds the port.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
75
118
|
## Collaborator Access Policy
|
|
76
119
|
|
|
77
120
|
This policy defines which collaborators can access which project data. Enforce boundaries at the source-control/project level and document scope explicitly to prevent drift.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
**Presence report (ARC-044 decision 3, ARC-060).** When a presence channel is configured on this
|
|
2
|
+
machine, report through it; when none is, this spell behaves exactly as it does without this
|
|
3
|
+
paragraph. Configured means `spell presence status` exits 0: the service is running and the token
|
|
4
|
+
in the user store opens it. Report with the CLI only; never pass a token on the command line, and
|
|
5
|
+
never write a repository file to report.
|
|
6
|
+
|
|
7
|
+
- At the start of this spell: `spell presence report --spell <spell-id> --status working --task "<the task in one line>"`.
|
|
8
|
+
- When this spell's output carries a `## ⚠ Needs you` block: `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line; the block itself is unchanged.
|
|
9
|
+
- At a checkpoint worth showing (a commit made, a story passed): `spell presence report --status working --task "<what is now happening>"`.
|
|
10
|
+
- When the session closes: `spell presence end`.
|
|
11
|
+
|
|
12
|
+
The command is silent and exits 0 when no channel is configured, prints one line to stderr and
|
|
13
|
+
still exits 0 when the channel refuses a report, and never blocks a spell. The service keeps
|
|
14
|
+
presence in memory only, reconstructable from `spell state --json` and never authoritative; a
|
|
15
|
+
session that stops reporting is shown as unknown after 90 seconds. Enforcement: explicitly
|
|
16
|
+
advisory prose (ARC-023) — no check reads a spell's shell history for these calls.
|
|
@@ -26,6 +26,25 @@ Use these files first:
|
|
|
26
26
|
- Most recent journal file(s) in [journal/](../../journal/)
|
|
27
27
|
- Any files changed during this session
|
|
28
28
|
|
|
29
|
+
<!-- fragment:presence-report:start -->
|
|
30
|
+
**Presence report (ARC-044 decision 3, ARC-060).** When a presence channel is configured on this
|
|
31
|
+
machine, report through it; when none is, this spell behaves exactly as it does without this
|
|
32
|
+
paragraph. Configured means `spell presence status` exits 0: the service is running and the token
|
|
33
|
+
in the user store opens it. Report with the CLI only; never pass a token on the command line, and
|
|
34
|
+
never write a repository file to report.
|
|
35
|
+
|
|
36
|
+
- At the start of this spell: `spell presence report --spell <spell-id> --status working --task "<the task in one line>"`.
|
|
37
|
+
- When this spell's output carries a `## ⚠ Needs you` block: `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line; the block itself is unchanged.
|
|
38
|
+
- At a checkpoint worth showing (a commit made, a story passed): `spell presence report --status working --task "<what is now happening>"`.
|
|
39
|
+
- When the session closes: `spell presence end`.
|
|
40
|
+
|
|
41
|
+
The command is silent and exits 0 when no channel is configured, prints one line to stderr and
|
|
42
|
+
still exits 0 when the channel refuses a report, and never blocks a spell. The service keeps
|
|
43
|
+
presence in memory only, reconstructable from `spell state --json` and never authoritative; a
|
|
44
|
+
session that stops reporting is shown as unknown after 90 seconds. Enforcement: explicitly
|
|
45
|
+
advisory prose (ARC-023) — no check reads a spell's shell history for these calls.
|
|
46
|
+
<!-- fragment:presence-report:end -->
|
|
47
|
+
|
|
29
48
|
Workflow:
|
|
30
49
|
|
|
31
50
|
1. **Reconstruct the full session** from conversation context, terminal history, and changed files. Identify:
|
|
@@ -24,6 +24,25 @@ Use these files first:
|
|
|
24
24
|
- [governance/git-conventions.md](../../.arcane/governance/git-conventions.md) — Conventional Commits reference and agent attribution model
|
|
25
25
|
- [DECISIONS.md](../../DECISIONS.md) — for ADR context if relevant
|
|
26
26
|
|
|
27
|
+
<!-- fragment:presence-report:start -->
|
|
28
|
+
**Presence report (ARC-044 decision 3, ARC-060).** When a presence channel is configured on this
|
|
29
|
+
machine, report through it; when none is, this spell behaves exactly as it does without this
|
|
30
|
+
paragraph. Configured means `spell presence status` exits 0: the service is running and the token
|
|
31
|
+
in the user store opens it. Report with the CLI only; never pass a token on the command line, and
|
|
32
|
+
never write a repository file to report.
|
|
33
|
+
|
|
34
|
+
- At the start of this spell: `spell presence report --spell <spell-id> --status working --task "<the task in one line>"`.
|
|
35
|
+
- When this spell's output carries a `## ⚠ Needs you` block: `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line; the block itself is unchanged.
|
|
36
|
+
- At a checkpoint worth showing (a commit made, a story passed): `spell presence report --status working --task "<what is now happening>"`.
|
|
37
|
+
- When the session closes: `spell presence end`.
|
|
38
|
+
|
|
39
|
+
The command is silent and exits 0 when no channel is configured, prints one line to stderr and
|
|
40
|
+
still exits 0 when the channel refuses a report, and never blocks a spell. The service keeps
|
|
41
|
+
presence in memory only, reconstructable from `spell state --json` and never authoritative; a
|
|
42
|
+
session that stops reporting is shown as unknown after 90 seconds. Enforcement: explicitly
|
|
43
|
+
advisory prose (ARC-023) — no check reads a spell's shell history for these calls.
|
|
44
|
+
<!-- fragment:presence-report:end -->
|
|
45
|
+
|
|
27
46
|
Workflow:
|
|
28
47
|
|
|
29
48
|
1. **Check git status and guard the branch** — run `git status` to see what changed, and confirm the current branch.
|
|
@@ -26,6 +26,25 @@ Use these files for context:
|
|
|
26
26
|
- The feature's `stories.json` — for the work breakdown
|
|
27
27
|
- The feature's `progress.txt` (in `features/{slug}/`, if present) — accumulated learnings from prior iterations; read it before each iteration and append new learnings as the loop proceeds
|
|
28
28
|
|
|
29
|
+
<!-- fragment:presence-report:start -->
|
|
30
|
+
**Presence report (ARC-044 decision 3, ARC-060).** When a presence channel is configured on this
|
|
31
|
+
machine, report through it; when none is, this spell behaves exactly as it does without this
|
|
32
|
+
paragraph. Configured means `spell presence status` exits 0: the service is running and the token
|
|
33
|
+
in the user store opens it. Report with the CLI only; never pass a token on the command line, and
|
|
34
|
+
never write a repository file to report.
|
|
35
|
+
|
|
36
|
+
- At the start of this spell: `spell presence report --spell <spell-id> --status working --task "<the task in one line>"`.
|
|
37
|
+
- When this spell's output carries a `## ⚠ Needs you` block: `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line; the block itself is unchanged.
|
|
38
|
+
- At a checkpoint worth showing (a commit made, a story passed): `spell presence report --status working --task "<what is now happening>"`.
|
|
39
|
+
- When the session closes: `spell presence end`.
|
|
40
|
+
|
|
41
|
+
The command is silent and exits 0 when no channel is configured, prints one line to stderr and
|
|
42
|
+
still exits 0 when the channel refuses a report, and never blocks a spell. The service keeps
|
|
43
|
+
presence in memory only, reconstructable from `spell state --json` and never authoritative; a
|
|
44
|
+
session that stops reporting is shown as unknown after 90 seconds. Enforcement: explicitly
|
|
45
|
+
advisory prose (ARC-023) — no check reads a spell's shell history for these calls.
|
|
46
|
+
<!-- fragment:presence-report:end -->
|
|
47
|
+
|
|
29
48
|
Workflow (loop until all stories pass):
|
|
30
49
|
|
|
31
50
|
0. **Sync workspace** — before any code changes, ensure the workspace is current:
|
|
@@ -84,6 +84,25 @@ The slug comes from the work — the focus, the handoff's active task, the top n
|
|
|
84
84
|
5. Report `Renamed <old> → <new>` in the run's output. If the worktree, the pull-request state or the remote cannot be determined, stop without renaming and ask the operator.
|
|
85
85
|
<!-- fragment:branch-rename-gate:end -->
|
|
86
86
|
|
|
87
|
+
<!-- fragment:presence-report:start -->
|
|
88
|
+
**Presence report (ARC-044 decision 3, ARC-060).** When a presence channel is configured on this
|
|
89
|
+
machine, report through it; when none is, this spell behaves exactly as it does without this
|
|
90
|
+
paragraph. Configured means `spell presence status` exits 0: the service is running and the token
|
|
91
|
+
in the user store opens it. Report with the CLI only; never pass a token on the command line, and
|
|
92
|
+
never write a repository file to report.
|
|
93
|
+
|
|
94
|
+
- At the start of this spell: `spell presence report --spell <spell-id> --status working --task "<the task in one line>"`.
|
|
95
|
+
- When this spell's output carries a `## ⚠ Needs you` block: `spell presence report --status waiting --needs-you "<line 1>" --needs-you "<line 2>"`, one flag per line; the block itself is unchanged.
|
|
96
|
+
- At a checkpoint worth showing (a commit made, a story passed): `spell presence report --status working --task "<what is now happening>"`.
|
|
97
|
+
- When the session closes: `spell presence end`.
|
|
98
|
+
|
|
99
|
+
The command is silent and exits 0 when no channel is configured, prints one line to stderr and
|
|
100
|
+
still exits 0 when the channel refuses a report, and never blocks a spell. The service keeps
|
|
101
|
+
presence in memory only, reconstructable from `spell state --json` and never authoritative; a
|
|
102
|
+
session that stops reporting is shown as unknown after 90 seconds. Enforcement: explicitly
|
|
103
|
+
advisory prose (ARC-023) — no check reads a spell's shell history for these calls.
|
|
104
|
+
<!-- fragment:presence-report:end -->
|
|
105
|
+
|
|
87
106
|
**Drift Check (run before deep context gathering):**
|
|
88
107
|
|
|
89
108
|
Run the `spell-check-drift` spell first, before building the state snapshot or gathering deep context. Drift compounds the longer a session runs, so catch it early.
|