@naxodev/apnea 0.2.0 → 0.2.2
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 +18 -1
- package/SECURITY.md +36 -0
- package/briefs/orchestrator.md +4 -3
- package/dist/cli.js +8375 -15093
- package/docs/protocol/artifacts.md +18 -2
- package/docs/protocol/config.md +15 -3
- package/docs/protocol/manual-gate.md +8 -8
- package/docs/protocol/overview.md +17 -4
- package/extension/adapters/commit.ts +5 -1
- package/extension/adapters/dispatch.ts +9 -1
- package/extension/adapters/setup.ts +15 -1
- package/extension/adapters/start.ts +5 -1
- package/extension/adapters/status.ts +17 -2
- package/extension/adapters/wait.ts +6 -1
- package/extension/api.ts +7 -1
- package/extension/cli/main.ts +67 -7
- package/extension/cli/parse.ts +172 -5
- package/extension/domain/paths.ts +2 -11
- package/extension/domain/timeouts.ts +4 -0
- package/extension/domain/types.ts +65 -3
- package/extension/errors.ts +51 -16
- package/extension/operation-hooks.ts +6 -0
- package/extension/registry.ts +29 -15
- package/extension/run-tool.ts +19 -2
- package/extension/schema/config.ts +58 -16
- package/extension/schema/frontmatter.ts +57 -0
- package/extension/schema/state.ts +210 -13
- package/extension/services/app-live.ts +2 -1
- package/extension/services/config.ts +6 -4
- package/extension/services/file-system.ts +346 -75
- package/extension/services/herdr.ts +466 -260
- package/extension/services/operation-lock.ts +452 -0
- package/extension/services/process.ts +477 -0
- package/extension/services/run-store.ts +38 -16
- package/extension/services/vcs.ts +1258 -328
- package/extension/workflows/commit.ts +214 -13
- package/extension/workflows/dispatch.ts +305 -67
- package/extension/workflows/setup.ts +59 -32
- package/extension/workflows/start.ts +6 -4
- package/extension/workflows/status.ts +2 -2
- package/extension/workflows/wait.ts +62 -77
- package/package.json +2 -2
- package/schemas/config.schema.json +5 -1
- package/schemas/state.schema.json +165 -11
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ of them went stale; a reader could not tell which.
|
|
|
86
86
|
| `start <goal>` | `workflow_start` | `[--allow-dirty] [--slug=name]` _(CLI only)_ | start a run |
|
|
87
87
|
| `resume` / `abandon` | `workflow_start` | | resume or abandon |
|
|
88
88
|
| `status` | `workflow_status` | | read-only snapshot |
|
|
89
|
-
| `dispatch <kind>` | `dispatch_role` | `[--rework]`
|
|
89
|
+
| `dispatch <kind>` | `dispatch_role` | `[--rework] [--redeliver]` | launch or explicitly redeliver a role |
|
|
90
90
|
| `wait` | `workflow_wait` | `[--poll=<ms>] [--budget=<ms>]` _(CLI only)_ | wait for the pending artifact |
|
|
91
91
|
| `commit [message]` | `workflow_commit_phase` | `[--done]` | verify + commit phase |
|
|
92
92
|
| `reset-rounds <gate>` | — | `[--i-am-human]` _(CLI only)_ | **human only** |
|
|
@@ -94,6 +94,18 @@ of them went stale; a reader could not tell which.
|
|
|
94
94
|
Prefix with `/` inside Pi (`/apnea status`), or run it as a shell command (`apnea status`).
|
|
95
95
|
`/apnea-start` and `/apnea-status` are short aliases.
|
|
96
96
|
|
|
97
|
+
`--rework` remains through 0.2.x as a deprecated assertion. Persisted review state selects and
|
|
98
|
+
authorizes rework even when callers omit the flag. Caller input grants authority only for
|
|
99
|
+
ambiguous version-1 plan or code migration. `--redeliver` reuses matching pending ownership without
|
|
100
|
+
advancing the round. Use it only after proving the prior pane is dead; a manual dispatch with no
|
|
101
|
+
pane requires the operator to request redelivery explicitly. Apnea persists whether pending work
|
|
102
|
+
crossed a manual or interactive boundary. Interactive ownership without a saved pane id refuses as
|
|
103
|
+
ambiguous. Legacy null-pane ownership has no safe discriminator and also refuses redelivery.
|
|
104
|
+
Before any redelivery checks or mutations, Apnea reads the pending artifact. A complete artifact
|
|
105
|
+
refuses redelivery and directs the caller to `workflow_wait`; review artifacts count as complete only
|
|
106
|
+
with a valid verdict and legal, schema-valid rework metadata. Malformed or incomplete artifacts
|
|
107
|
+
continue through normal liveness validation.
|
|
108
|
+
|
|
97
109
|
`reset-rounds` is not a Pi tool. It exists only as `apnea reset-rounds` and `/apnea reset-rounds`.
|
|
98
110
|
Only the CLI gates it — it refuses unless stdin/stdout are a terminal and a human retypes the gate
|
|
99
111
|
key, or passes `--i-am-human`. The slash command has no such gate: `/apnea` is already a human at
|
|
@@ -109,9 +121,14 @@ See [`docs/protocol/config.md`](docs/protocol/config.md) for the budget-floor ar
|
|
|
109
121
|
```text
|
|
110
122
|
/apnea setup # ~/.config/apnea/config.json from PATH
|
|
111
123
|
/apnea setup --project # also .apnea/config.json role→profile only
|
|
124
|
+
/apnea setup --force # replace profiles; required to replace malformed global JSON
|
|
112
125
|
/apnea setup --agents-md # also write/refresh an AGENTS.md loop primer at the repo root
|
|
113
126
|
```
|
|
114
127
|
|
|
128
|
+
Setup never silently destroys malformed JSON. Without `--force`, malformed existing global JSON
|
|
129
|
+
is left byte-for-byte unchanged and setup refuses. `--force` atomically replaces it and reports
|
|
130
|
+
the replacement. Malformed existing project config always fails closed.
|
|
131
|
+
|
|
115
132
|
After upgrading from a version with floating panes, old copied
|
|
116
133
|
`~/.config/apnea/herdr-plugin` files are inert. Apnea does not unlink or delete them; remove that
|
|
117
134
|
directory manually if you no longer need it.
|
package/SECURITY.md
CHANGED
|
@@ -17,6 +17,42 @@ this tool at a repository.
|
|
|
17
17
|
- **Planner-authored verify commands execute at the commit gate**, in the project working
|
|
18
18
|
directory, in the same trust domain as the coder writing source. Their output goes to
|
|
19
19
|
`verify.log`.
|
|
20
|
+
- **Git phase commits bypass hooks by design.** Apnea stages into an isolated index, writes and
|
|
21
|
+
validates the exact tree, creates it with `commit-tree`, then compare-and-swaps the branch ref.
|
|
22
|
+
Running hooks after validation would let them change the committed tree. Apnea preserves
|
|
23
|
+
`commit.gpgsign=true` by asking `commit-tree` to sign, but it does not run commit-msg,
|
|
24
|
+
pre-commit, or post-commit policy hooks. The validated tree is written to the real index before
|
|
25
|
+
the ref moves, so index failure cannot advance the branch. An unrelated external Git process can
|
|
26
|
+
still race that real-index replacement; the repository lock coordinates Apnea processes, not
|
|
27
|
+
arbitrary Git clients.
|
|
28
|
+
- **Repository locks identify owners by PID plus a random token.** Apnea reclaims a valid dead
|
|
29
|
+
owner after the grace period. An atomic `<lock-path>.reclaim` directory serializes stale
|
|
30
|
+
removers across their token check and canonical rename, preserving every replacement owner.
|
|
31
|
+
A crashed remover can leave this guard behind. Apnea never reclaims the guard automatically:
|
|
32
|
+
doing so would introduce the same ownership race. If an error reports a stranded guard, stop
|
|
33
|
+
all Apnea processes using that lock before manually removing the reported guard path.
|
|
34
|
+
Malformed locks require manual cleanup after verifying that no owner remains. PID reuse can
|
|
35
|
+
keep a dead lock looking live because there is no portable process-creation identity across
|
|
36
|
+
supported platforms; this also fails closed. Global setup waits only when the recorded PID is
|
|
37
|
+
currently live; it never retries stale or malformed ownership.
|
|
38
|
+
- **Setup serializes account-global configuration and role resources.** Every setup holds one
|
|
39
|
+
same-user lock keyed by the canonical account home for its full read/merge/write and role-agent
|
|
40
|
+
materialization. Setup takes this global lock before an optional repository lock. Global config
|
|
41
|
+
replacement is atomic, rejects symlink components, and fsyncs the file and parent directory
|
|
42
|
+
where the platform supports it. Malformed global JSON remains byte-for-byte unchanged unless a
|
|
43
|
+
human passes `--force`; the result explicitly reports a forced replacement. Malformed project
|
|
44
|
+
config always fails closed.
|
|
45
|
+
- **Persisted inputs are bounded regular UTF-8 files.** Config, state, setup `AGENTS.md`, and
|
|
46
|
+
workflow artifacts are opened once and read through the descriptor. Apnea rejects non-regular
|
|
47
|
+
files, invalid UTF-8, and inputs over 1 MiB before parsing. Descriptor checks avoid a separate
|
|
48
|
+
stat/read race. Existing project and account-home symlink confinement still applies.
|
|
49
|
+
- **State-owned artifact paths cannot escape the repository.** `pending_artifact`,
|
|
50
|
+
`current_phase_package`, and `current_code_review` must be relative paths below `.apnea/`.
|
|
51
|
+
Absolute paths, NULs, backslashes, empty components, and `.` or `..` components make state
|
|
52
|
+
corrupt.
|
|
53
|
+
- **Project-path checks reject existing symlink components but cannot provide `openat` semantics.**
|
|
54
|
+
Node does not expose a portable directory-descriptor traversal API, so a hostile same-user
|
|
55
|
+
process can still race a checked parent component before the final filesystem operation.
|
|
20
56
|
|
|
21
57
|
See [`docs/protocol/config.md`](docs/protocol/config.md) (Trust model section) and
|
|
22
58
|
[`docs/adr/0006-config-trust-model.md`](docs/adr/0006-config-trust-model.md) for the full
|
package/briefs/orchestrator.md
CHANGED
|
@@ -13,11 +13,11 @@ You schedule an Apnea **Run**. You do not implement product code, edit app sourc
|
|
|
13
13
|
1. Start run (clean tree unless allow-dirty / resume). **Start only writes state — do not stop here.**
|
|
14
14
|
2. Immediately dispatch **planner** (`kind=plan`) → wait for plan artifact.
|
|
15
15
|
3. Dispatch **reviewer** (plan review) → wait for verdict.
|
|
16
|
-
4. On `CHANGES_REQUIRED`,
|
|
16
|
+
4. On `CHANGES_REQUIRED`, dispatch planner then reviewer. Persisted state advances the round.
|
|
17
17
|
5. On `APPROVED`, dispatch planner for **phase package**.
|
|
18
18
|
6. Dispatch **coder** with phase package path.
|
|
19
19
|
7. Dispatch **reviewer** (code review) → wait.
|
|
20
|
-
8. On `CHANGES_REQUIRED` with `rework: code` or no rework field,
|
|
20
|
+
8. On `CHANGES_REQUIRED` with `rework: code` or no rework field, dispatch coder, then reviewer. Persisted state advances the round.
|
|
21
21
|
9. On `CHANGES_REQUIRED` with `rework: phase_package`, dispatch planner for the revised phase package, then coder and reviewer. The package dispatch advances the review round automatically.
|
|
22
22
|
10. On `APPROVED`, commit phase (run verify commands first).
|
|
23
23
|
11. Repeat from phase packaging until planner reports no remaining phases.
|
|
@@ -26,6 +26,7 @@ You schedule an Apnea **Run**. You do not implement product code, edit app sourc
|
|
|
26
26
|
## Rules
|
|
27
27
|
|
|
28
28
|
- One outstanding Dispatch at a time.
|
|
29
|
+
- Never infer or authorize rework from caller input, except an explicit `rework=true` assertion for ambiguous version-1 plan or code state. `workflow_wait` persists every new required target, and `dispatch_role` consumes it.
|
|
29
30
|
- Read verdicts only from artifact front-matter.
|
|
30
31
|
- Never push remotes or open PRs.
|
|
31
32
|
|
|
@@ -40,7 +41,7 @@ don't, the orchestrator still owns recovery.
|
|
|
40
41
|
- Prompt sitting in the input / INSERT / "Pasted text" → `herdr pane send-keys <id> Enter`.
|
|
41
42
|
- Agent idle, no artifact → `herdr pane run <id>` with a short nudge naming the exact artifact path.
|
|
42
43
|
- Agent working / API retrying → `workflow_wait` again (do not re-dispatch yet).
|
|
43
|
-
- Pane missing / harness exited to bare shell → `dispatch_role` same kind (no rework flag).
|
|
44
|
+
- Pane missing / harness exited to bare shell → `dispatch_role` same kind with `redeliver=true` (no rework flag).
|
|
44
45
|
3. Only escalate after recovery failed twice, or on: round cap, dirty reviewer tree, illegal step, VCS confusion.
|
|
45
46
|
|
|
46
47
|
## Escalate (after recovery fails)
|