You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(identic-ai): seed board-gate correctly, default heartbeats to on-call
Three seed/brief fixes, all verified live against Paperclip v2026.529.0:
- Board gate: requireBoardApprovalForNewAgents is silently dropped from the
POST /api/companies create body (a company created with it true comes back
false). Following the seed literally produced an UNGOVERNED sandbox with no
error -- the approval gate the whole course governs was off. Move the gate to
a PATCH-after-seed step (seed_order + post_seed) and add a verify step;
document the rule in AGENTS.md and the verification checklist.
- Heartbeats: workers shipped with the interval timer ON (enabled:true, 60s),
contradicting the course's own "timer off, on call" guidance and a budget
footgun if a worker is switched to an LLM adapter. Default to enabled:false,
wakeOnDemand:true.
- Approval types: demo_approval_flood now maps the friendly domain names
(refund/hire/termination) to Paperclip's real enum (request_board_approval/
hire_agent/budget_override_required); posting the domain names verbatim fails
server validation.
Also re-confirmed the ledger rationale holds in v2026.529.0 (approvals still has
decided_by_user_id, no decided_by_agent_id) and clarified that worker
`capabilities` is descriptive, not enforced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: identic-ai/AGENTS.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -113,10 +113,11 @@ The envelope lives at `~/.openclaw/governance/delegated-envelope.json` (owner-ed
113
113
-**Never commit the signing private key.** The ed25519 private key (`~/.openclaw/keys/identic-ai.pem`, chmod 600, or the macOS Keychain) is never printed, logged, copied to clipboard, or written to git. Public key only. The base `.gitignore` already ignores `*.pem` and `keys/`; do not undo that.
114
114
-**Never write a production ledger from a test.** During dry-run and any `/tmp` test, write to a throwaway Neon branch or a local table, never the owner's real `governance_ledger`. Provisioning and migration go through the Neon MCP on a branch (`prepare_database_migration`, then `complete_database_migration`), never untested DDL against main.
115
115
-**Board key vs agent key.** A delegate acting **as the board** (resolving approvals) uses local-trusted mode (the sandbox, no key) or a board API key. A delegate registered **as a Paperclip agent** (`agent_api_keys`, `PAPERCLIP_API_KEY`) **cannot decide approvals at all**: the three decision routes are `assertBoard`-gated and reject an agent key with 403. So Claudia resolves approvals through the board path. Do not try to make an agent key approve; it fails by design.
116
-
-**Paperclip does not attribute the principal; that is why you build the ledger.** Verified against Paperclip v2026.525.0: the approval-decision routes (`/approvals/:id/approve|reject|request-revision`) hardcode `actorType:"user"` and never write an `agentId`; the `approvals` table has `decidedByUserId` and no `decidedByAgentId`. So `activity_log` cannot tell the owner-human from the owner's delegate on a decision, and it is unsigned. The `governance_ledger` + the ed25519 attestation carry the human-vs-delegate distinction and the provenance that Paperclip does not record. Frame it honestly to the owner: Paperclip's audit trail is real and immutable, but on a decision it only knows "a board user did this"; your ledger adds the attested principal.
116
+
-**Paperclip does not attribute the principal; that is why you build the ledger.** Verified against Paperclip v2026.525.0: the approval-decision routes (`/approvals/:id/approve|reject|request-revision`) hardcode `actorType:"user"` and never write an `agentId`; the `approvals` table has `decidedByUserId` and no `decidedByAgentId` (re-confirmed live against v2026.529.0, four releases newer: the column set is still `decided_by_user_id` with no `decided_by_agent_id`). So `activity_log` cannot tell the owner-human from the owner's delegate on a decision, and it is unsigned. The `governance_ledger` + the ed25519 attestation carry the human-vs-delegate distinction and the provenance that Paperclip does not record. Frame it honestly to the owner: Paperclip's audit trail is real and immutable, but on a decision it only knows "a board user did this"; your ledger adds the attested principal.
117
117
-**Canonical JSON or signatures fail.** Sign over RFC-8785 (JCS) canonical JSON: sort keys, strip insignificant whitespace, so signer and verifier byte-match. A signature over a re-serialized-differently payload will not verify. The `sign-decision` skill in `worked-examples/` does this; match its canonicalization exactly.
118
118
-**Never trust a CLI exit code of 0 alone.** Several Paperclip CLI subcommands print parent help and exit 0 on an unknown verb. Read the output. When the API or MCP is the reliable path, prefer it.
119
119
-**The `paperclip` MCP only resolves while the local sandbox runs.** No Paperclip tools before you `paperclipai onboard --yes` is expected.
120
+
-**The board-approval gate is PATCH-only — set it AFTER seeding, then verify.**`requireBoardApprovalForNewAgents` is **silently dropped** from the `POST /api/companies` create body (verified live, v2026.529.0: a company created with it `true` comes back `false`). So seed the company and all four workers first, **then**`PATCH /api/companies/:id` with `{ "requireBoardApprovalForNewAgents": true }`, **then confirm it took** — a `GET` shows `true`, or a direct `POST /agents` now returns `409`. Skip this and the sandbox runs **ungoverned with no error**, which silently defeats the whole course (the approval gate Claudia governs would be off). `seed-company.json` encodes this as `seed_order` + `post_seed`.
120
121
121
122
---
122
123
@@ -144,7 +145,7 @@ The build is Part 4's seven Decisions. Read the page section for each; plan, the
144
145
145
146
## Verification (what "done" means)
146
147
147
-
-**Setup:**`node --version` is 22.16+; with the sandbox running you see the `paperclip` MCP tools; the Neon OAuth click succeeded and `neon-postgres` can `run_sql`.
148
+
-**Setup:**`node --version` is 22.16+; with the sandbox running you see the `paperclip` MCP tools; the Neon OAuth click succeeded and `neon-postgres` can `run_sql`; the seeded company has `requireBoardApprovalForNewAgents: true` (confirm with a `GET`, or a direct `POST /agents` returns `409`) — proof the board gate is actually on and was not silently dropped at create.
148
149
-**Signing:** the `sign-decision` skill produces a signature that an independent verify step confirms against the public key, over canonical JSON that byte-matches.
149
150
-**Delegation:** a refund inside the envelope is auto-resolved with a signed ledger row; one outside it is surfaced, not posted, and the refusal is also logged.
150
151
-**Audit:** the `governance_ledger` query returns, for one approval id, both the Paperclip `activity_log` row (`actor_type='user'`) and the ledger row naming `principal='owner_identic_ai'` with its attestation. The two together are the full story.
Copy file name to clipboardExpand all lines: identic-ai/seed-company.json
+12-8Lines changed: 12 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,14 @@
1
1
{
2
-
"note": "Maya's company, as a seed for the local Paperclip sandbox. The agent loads this so a reader with no Course 5-7 deployment has a gradable company on day one. Field shapes follow Paperclip's verified create bodies (POST /api/companies, then POST /api/companies/:id/agents). Confirm the exact field set against the running install's GET /llms/agent-configuration/<adapterType>.txt before posting; Paperclip drifts. The four Workers below run on the cheap no-LLM `process` adapter by default so the sandbox costs nothing to seed; switch a Worker to a local LLM adapter only when a scenario needs real reasoning.",
2
+
"note": "Maya's company, as a seed for the local Paperclip sandbox. The agent loads this so a reader with no Course 5-7 deployment has a gradable company on day one. Field shapes follow Paperclip's verified create bodies (POST /api/companies, then POST /api/companies/:id/agents). Confirm the exact field set against the running install's GET /llms/agent-configuration/<adapterType>.txt before posting; Paperclip drifts. The four Workers below run on the cheap no-LLM `process` adapter by default so the sandbox costs nothing to seed; switch a Worker to a local LLM adapter only when a scenario needs real reasoning. Heartbeats below ship OFF with wake-on-demand ON — the recommended 'on call, not ticking' default (an interval timer wakes a worker on a clock even when there is no work, and burns budget the moment a worker is switched to an LLM adapter). Note: `capabilities` is DESCRIPTIVE text, not an enforced limit. Paperclip enforces authority through grants/scope/approvals; in this course the delegated envelope enforces the refund bands, so the dollar figures in capabilities are the human-readable description, not a hard cap Paperclip checks.",
3
+
"seed_order": "Seed in THIS order — it matters. (1) Create the company: POST /api/companies (do NOT include requireBoardApprovalForNewAgents here; it is PATCH-only and is silently dropped from the create body). (2) Seed each worker: POST /api/companies/:id/agents (succeeds while the gate is off). (3) Turn the board gate ON: PATCH /api/companies/:id with { \"requireBoardApprovalForNewAgents\": true }. (4) VERIFY it is on: GET the company returns true, or a direct POST /agents now returns 409. Skip (3) or (4) and the sandbox is UNGOVERNED — the approval gate this whole course is about would be silently off, with no error to tell you.",
3
4
"company": {
4
5
"name": "Maya Customer Support",
5
6
"description": "Respond to customer inquiries within 4 hours, with refund and budget decisions made consistently and within policy. The owner (Maya) governs through an approval gate; this course delegates the routine slice of that gate to her Identic AI.",
6
-
"budgetMonthlyCents": 800000,
7
-
"requireBoardApprovalForNewAgents": true
7
+
"budgetMonthlyCents": 800000
8
+
},
9
+
"post_seed": {
10
+
"requireBoardApprovalForNewAgents": true,
11
+
"note": "Apply this via PATCH /api/companies/:id AFTER all workers are seeded (see seed_order). PATCH-only: Paperclip silently ignores requireBoardApprovalForNewAgents in the create body, so setting it at create time leaves the board-approval gate OFF with no error — and an ungoverned sandbox defeats the course."
8
12
},
9
13
"workers": [
10
14
{
@@ -21,7 +25,7 @@
21
25
"budgetMonthlyCents": 100000,
22
26
"runtimeConfig": {
23
27
"heartbeat": {
24
-
"enabled": true,
28
+
"enabled": false,
25
29
"intervalSec": 60,
26
30
"wakeOnDemand": true
27
31
}
@@ -41,7 +45,7 @@
41
45
"budgetMonthlyCents": 150000,
42
46
"runtimeConfig": {
43
47
"heartbeat": {
44
-
"enabled": true,
48
+
"enabled": false,
45
49
"intervalSec": 60,
46
50
"wakeOnDemand": true
47
51
}
@@ -61,7 +65,7 @@
61
65
"budgetMonthlyCents": 80000,
62
66
"runtimeConfig": {
63
67
"heartbeat": {
64
-
"enabled": true,
68
+
"enabled": false,
65
69
"intervalSec": 60,
66
70
"wakeOnDemand": true
67
71
}
@@ -81,15 +85,15 @@
81
85
"budgetMonthlyCents": 300000,
82
86
"runtimeConfig": {
83
87
"heartbeat": {
84
-
"enabled": true,
88
+
"enabled": false,
85
89
"intervalSec": 60,
86
90
"wakeOnDemand": true
87
91
}
88
92
}
89
93
}
90
94
],
91
95
"demo_approval_flood": {
92
-
"note": "Decision 6 seeds a flood of pending approvals so Claudia can auto-clear the routine band and surface the consequential ones. Generate these as real Paperclip approvals (POST /api/companies/:id/approvals) at run time, modeled on the bands in course-seven-export/approvals.json. A reasonable demo flood: ~15 refunds spread across the bands Maya clears fast (small duplicate-charge and billing-error refunds), 2-3 that sit outside the delegated envelope (a large refund on a new account with prior refunds; a budget override above the delegated cap), so the surfaced set is non-empty.",
96
+
"note": "Decision 6 seeds a flood of pending approvals so Claudia can auto-clear the routine band and surface the consequential ones. Generate these as real Paperclip approvals (POST /api/companies/:id/approvals) at run time, modeled on the bands in course-seven-export/approvals.json. A reasonable demo flood: ~15 refunds spread across the bands Maya clears fast (small duplicate-charge and billing-error refunds), 2-3 that sit outside the delegated envelope (a large refund on a new account with prior refunds; a budget override above the delegated cap), so the surfaced set is non-empty. MAP the domain names to Paperclip's real approval `type` enum when you POST: a refund -> `request_board_approval` (put the refund amount/context in `payload`); a budget override -> `budget_override_required`; a hire -> `hire_agent`. There is no `refund`/`hire`/`termination` approval type — posting those verbatim fails server validation. course-seven-export/approvals.json uses the friendly domain names for the judgment seed; translate them here.",
0 commit comments