Skip to content

Commit bfbabdf

Browse files
mjunaidcaclaude
andcommitted
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>
1 parent ecbfdcd commit bfbabdf

2 files changed

Lines changed: 15 additions & 10 deletions

File tree

identic-ai/AGENTS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,11 @@ The envelope lives at `~/.openclaw/governance/delegated-envelope.json` (owner-ed
113113
- **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.
114114
- **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.
115115
- **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.
117117
- **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.
118118
- **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.
119119
- **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`.
120121

121122
---
122123

@@ -144,7 +145,7 @@ The build is Part 4's seven Decisions. Read the page section for each; plan, the
144145

145146
## Verification (what "done" means)
146147

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.
148149
- **Signing:** the `sign-decision` skill produces a signature that an independent verify step confirms against the public key, over canonical JSON that byte-matches.
149150
- **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.
150151
- **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.

identic-ai/seed-company.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
{
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.",
34
"company": {
45
"name": "Maya Customer Support",
56
"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."
812
},
913
"workers": [
1014
{
@@ -21,7 +25,7 @@
2125
"budgetMonthlyCents": 100000,
2226
"runtimeConfig": {
2327
"heartbeat": {
24-
"enabled": true,
28+
"enabled": false,
2529
"intervalSec": 60,
2630
"wakeOnDemand": true
2731
}
@@ -41,7 +45,7 @@
4145
"budgetMonthlyCents": 150000,
4246
"runtimeConfig": {
4347
"heartbeat": {
44-
"enabled": true,
48+
"enabled": false,
4549
"intervalSec": 60,
4650
"wakeOnDemand": true
4751
}
@@ -61,7 +65,7 @@
6165
"budgetMonthlyCents": 80000,
6266
"runtimeConfig": {
6367
"heartbeat": {
64-
"enabled": true,
68+
"enabled": false,
6569
"intervalSec": 60,
6670
"wakeOnDemand": true
6771
}
@@ -81,15 +85,15 @@
8185
"budgetMonthlyCents": 300000,
8286
"runtimeConfig": {
8387
"heartbeat": {
84-
"enabled": true,
88+
"enabled": false,
8589
"intervalSec": 60,
8690
"wakeOnDemand": true
8791
}
8892
}
8993
}
9094
],
9195
"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.",
9397
"suggested_counts": {
9498
"routine_inside_envelope": 15,
9599
"consequential_to_surface": 3

0 commit comments

Comments
 (0)