@agenticprimitives/devkit 0.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +62 -0
- package/bin/ap.mjs +119 -0
- package/dist/config.d.ts +130 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/dispatch.d.ts +44 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +43 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +71 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/fs.d.ts +20 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +54 -0
- package/dist/fs.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lock.d.ts +18 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +15 -0
- package/dist/lock.js.map +1 -0
- package/dist/redact.d.ts +2 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +14 -0
- package/dist/redact.js.map +1 -0
- package/dist/rules/archetype-catalog.d.ts +4 -0
- package/dist/rules/archetype-catalog.d.ts.map +1 -0
- package/dist/rules/archetype-catalog.js +48 -0
- package/dist/rules/archetype-catalog.js.map +1 -0
- package/dist/rules/capability-vocabulary.d.ts +4 -0
- package/dist/rules/capability-vocabulary.d.ts.map +1 -0
- package/dist/rules/capability-vocabulary.js +56 -0
- package/dist/rules/capability-vocabulary.js.map +1 -0
- package/dist/rules/custodian-coverage.d.ts +4 -0
- package/dist/rules/custodian-coverage.d.ts.map +1 -0
- package/dist/rules/custodian-coverage.js +47 -0
- package/dist/rules/custodian-coverage.js.map +1 -0
- package/dist/rules/demo-person-org-distinct.d.ts +4 -0
- package/dist/rules/demo-person-org-distinct.d.ts.map +1 -0
- package/dist/rules/demo-person-org-distinct.js +26 -0
- package/dist/rules/demo-person-org-distinct.js.map +1 -0
- package/dist/rules/fail-open-knobs.d.ts +4 -0
- package/dist/rules/fail-open-knobs.d.ts.map +1 -0
- package/dist/rules/fail-open-knobs.js +64 -0
- package/dist/rules/fail-open-knobs.js.map +1 -0
- package/dist/rules/index.d.ts +10 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +29 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/internal-marker-split.d.ts +4 -0
- package/dist/rules/internal-marker-split.d.ts.map +1 -0
- package/dist/rules/internal-marker-split.js +31 -0
- package/dist/rules/internal-marker-split.js.map +1 -0
- package/dist/rules/lock-coherent.d.ts +4 -0
- package/dist/rules/lock-coherent.d.ts.map +1 -0
- package/dist/rules/lock-coherent.js +39 -0
- package/dist/rules/lock-coherent.js.map +1 -0
- package/dist/rules/no-cross-subject-vault-reads.d.ts +4 -0
- package/dist/rules/no-cross-subject-vault-reads.d.ts.map +1 -0
- package/dist/rules/no-cross-subject-vault-reads.js +35 -0
- package/dist/rules/no-cross-subject-vault-reads.js.map +1 -0
- package/dist/rules/no-hmac-home-bridge.d.ts +4 -0
- package/dist/rules/no-hmac-home-bridge.d.ts.map +1 -0
- package/dist/rules/no-hmac-home-bridge.js +40 -0
- package/dist/rules/no-hmac-home-bridge.js.map +1 -0
- package/dist/rules/no-private-keys.d.ts +4 -0
- package/dist/rules/no-private-keys.d.ts.map +1 -0
- package/dist/rules/no-private-keys.js +45 -0
- package/dist/rules/no-private-keys.js.map +1 -0
- package/dist/rules/rules-drift.d.ts +4 -0
- package/dist/rules/rules-drift.d.ts.map +1 -0
- package/dist/rules/rules-drift.js +46 -0
- package/dist/rules/rules-drift.js.map +1 -0
- package/dist/rules/types.d.ts +50 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/web-no-direct-mcp.d.ts +4 -0
- package/dist/rules/web-no-direct-mcp.d.ts.map +1 -0
- package/dist/rules/web-no-direct-mcp.js +37 -0
- package/dist/rules/web-no-direct-mcp.js.map +1 -0
- package/dist/rules-projection.d.ts +58 -0
- package/dist/rules-projection.d.ts.map +1 -0
- package/dist/rules-projection.js +135 -0
- package/dist/rules-projection.js.map +1 -0
- package/dist/upgrade.d.ts +37 -0
- package/dist/upgrade.d.ts.map +1 -0
- package/dist/upgrade.js +116 -0
- package/dist/upgrade.js.map +1 -0
- package/dist/workspace.d.ts +18 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +56 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +73 -0
- package/rules/coordination-vs-orchestration.md +31 -0
- package/rules/demo-person-org-distinct.md +40 -0
- package/rules/external-registry-integrations.md +15 -0
- package/rules/hard-rules.md +50 -0
- package/rules/one-capability-model-generates-both.md +82 -0
- package/rules/ontology-drives-behavior.md +58 -0
- package/rules/service-agent-signing.md +84 -0
- package/rules/sidecar.json +13 -0
- package/rules/skill-terminology.md +76 -0
- package/rules/vault-and-ontology.md +8 -0
- package/rules/vault-records-are-ontology-shaped.md +85 -0
- package/spec.md +11 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# A demo person is never also their organization
|
|
2
|
+
|
|
3
|
+
**Applies to every demo identity** in this repo and every derived roster (`engage`, `skills`, `uupg`).
|
|
4
|
+
|
|
5
|
+
## One sentence
|
|
6
|
+
|
|
7
|
+
A Person Smart Agent and an Organization Smart Agent are different classes (ADR-0046). **Never
|
|
8
|
+
point both labels at the same address** — not as a seed shortcut, not “his own SA”, not to make
|
|
9
|
+
`isOperator(address)` cheaper.
|
|
10
|
+
|
|
11
|
+
## Why this keeps getting re-derived
|
|
12
|
+
|
|
13
|
+
An org that is “just the person” skips a deploy. The operator check then compares the caller to
|
|
14
|
+
that one address and everything goes green. Home’s workspace switcher then walks the person as
|
|
15
|
+
if they were a parent org (`you → unnamed → unnamed → workspace`), Gather27 treats David’s
|
|
16
|
+
home as Gather, and stewardship/vault gates cannot tell custody of a person from custody of
|
|
17
|
+
an org.
|
|
18
|
+
|
|
19
|
+
The check that would have caught it is “are these two addresses equal?” — not “does the demo
|
|
20
|
+
sign in.”
|
|
21
|
+
|
|
22
|
+
## The pattern
|
|
23
|
+
|
|
24
|
+
1. **Person SA** = the human demo identity (`alice`, `david`, …).
|
|
25
|
+
2. **Org SA** = a second, deployed account the person *custodies*. Named. Own vault.
|
|
26
|
+
3. **Stewardship** = `org → person` (and Home’s related-orgs link). Custody of the org’s
|
|
27
|
+
key is not the org.
|
|
28
|
+
4. Operator / allow-lists compare the **person** address (or a dedicated operator list),
|
|
29
|
+
never reuse the org field.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// ❌ BAD — one address, two classes
|
|
33
|
+
{ handle: 'david', sa: '0xfd13…', org: '0xfd13…', orgName: 'Gather' }
|
|
34
|
+
|
|
35
|
+
// ✅ GOOD — Alice already looks like this
|
|
36
|
+
{ handle: 'alice', sa: '0xc35c…', org: '0xe261…', orgName: 'Missio Nexus' }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Roster of record: [`demo/personas.json`](../../../demo/personas.json).
|
|
40
|
+
Enforced by `pnpm check:demo-person-org-distinct`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# External Registry Boundary
|
|
2
|
+
|
|
3
|
+
Agentic Primitives builds its own Ring 0 intent, discovery, registry, and agent-card standards in `packages/*`, `apps/contracts`, and `specs/*`.
|
|
4
|
+
|
|
5
|
+
Integrations with ERC-8004, GoDaddy ANS, Hashgraph/HCS registries, DNS-AID, OASF, Veramo, hosted discovery APIs, indexers, explorers, and registry publication/sync services belong in external repos that import `@agenticprimitives/*`.
|
|
6
|
+
|
|
7
|
+
Do not add protocol-specific integration code, conformance adapters, registry clients, publication flows, hosted-service wiring, or external protocol vocabulary as package semantics inside primitive packages or contracts.
|
|
8
|
+
|
|
9
|
+
It is acceptable to study those systems and port generic design principles: signed cards, binding proofs, registry lifecycle, skill claims, validation hooks, transparency logs, fee-gated inboxes, and discovery metadata. When doing so, express them as AP-native primitives with AP-native vocabulary and stable extension points.
|
|
10
|
+
|
|
11
|
+
Default direction:
|
|
12
|
+
|
|
13
|
+
- Build AP-native signed agent cards, registry-kit contracts, skill/attestation schemas, intent/resolution/matching primitives, and fulfillment evidence in this repo.
|
|
14
|
+
- Put ERC-8004/ANS/HCS/DNS/OASF/MCP Registry bridges and publication adapters in sibling integration repos.
|
|
15
|
+
- If a feature name or type only exists to satisfy one external protocol, route it out of Ring 0.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Hard rules (root CLAUDE.md)
|
|
2
|
+
|
|
3
|
+
- **Smart Agent address is the canonical identifier.** Every person, org, service agent, and treasury IS its ERC-4337 SA address. Names, profiles, ERC-8004 entries, ANS handles, HCS topics — all facets pointing AT the canonical address. Cross-package APIs take `Address`, not names. See [ADR-0010](docs/architecture/decisions/0010-smart-agent-canonical-identifier.md) + [spec 220](specs/220-agent-identity-bootstrap.md) for the bootstrap process (deploy → forced-unique name → custody → optional facets).
|
|
4
|
+
- **Every Smart Agent is a Person, an Organization, or a Service — nothing else.** PROV-O's trichotomy (`prov:Person`/`prov:Organization`/`prov:SoftwareAgent`), mirrored by `AgentType = 'person' | 'org' | 'service'` in `@agenticprimitives/types`. Treasury, indexer, relay, marketplace, data-source are **Service roles** (profile `serviceType` / app-level kinds), never new classes; UI groups by class and labels by role; app kinds map DOWN to a class, never up into the enum ([ADR-0046](docs/architecture/decisions/0046-agent-classification-person-org-service.md), specs 217/225 §6). **A demo person SA is never also their org SA** — do not reuse “his own SA” as the organization ([agent-rules/demo-person-org-distinct.md](docs/architecture/agent-rules/demo-person-org-distinct.md), `pnpm check:demo-person-org-distinct`).
|
|
5
|
+
- **Canonical identity persists; credentials rotate.** Passkeys, SIWE EOAs, hardware wallets are control credential facets — replaceable, not the identity. Credential add / replace / remove is a custody-policy-governed operation (trustee quorum, guardian quorum, multi-credential self-recovery, or multi-sig), NEVER a delegation. The SA address NEVER changes during credential recovery. Delegations issued by the SA remain valid after rotation. See [ADR-0011](docs/architecture/decisions/0011-credential-recovery-and-re-association.md) + [spec 221](specs/221-credential-recovery.md).
|
|
6
|
+
- **Specs precede non-trivial code.** New `specs/2XX-*.md` before architecture changes; the spec is the architect-of-record.
|
|
7
|
+
- **Always check smart-agent first.** Before designing any non-trivial capability look at the analog in `/home/barb/smart-agent`. New specs MUST include a "Reference: smart-agent patterns to port" section. Deliberate divergence must say why.
|
|
8
|
+
- **Package boundaries** are one-directional: `types ← identity-auth ← agent-account ← delegation ← mcp-runtime`, plus `key-custody → delegation`, `tool-policy → mcp-runtime`, and the custody-layer fork `types ← custody` (depended on by `agent-account` and `delegation`; see [spec 213](specs/213-custody-layer-carve-out.md) for the firewall). No back-edges. `tool-policy` and `types` are transport-agnostic (no MCP/A2A/LangChain/Vercel imports).
|
|
9
|
+
- **`AgentAccount.sol` is an ERC-7579 modular core — custody governance is a module; sessions + spend are inlined in v1 (ARCH-H4, honest state).** The custody surface (threshold / guardians / recovery / approvals) IS an installed EXECUTOR module (`CustodyPolicy`), and account-governance ops route through it (spec 300). But session acceptance and spend limits are currently INLINED in `AgentAccount.sol` (spend exists only as a delegation-layer caveat, not an account hook), and `MODULE_TYPE_FALLBACK` is unsupported — so the "everything is a module" framing is aspirational for those two. The direction (extract `SessionKeyValidator`, then a spend hook) stands, but do NOT assert sessions/spend are already modules. `specs/209` is the taxonomy target of record; when extracting a module, update it + this rule together. See memory `feedback_erc7579_module_architecture`.
|
|
10
|
+
- **HTTPS is required; mTLS is optional; admission is always required.** The normative external profile is A2A over HTTPS. mTLS is *additive transport evidence* on a separate hostname (`mtls.<host>`) — it never establishes owning person, organization, canonical agent, relationship, delegation, purpose, capability, consent, or policy, and never lets a request skip a stage. SPIFFE is an optional workload binding, not identity: which agent a workload may act as is a signed `WorkloadIdentityBinding`, never an inference from a trust-domain path. MCP is a PRIVATE capability interface behind an admitted runtime, absent from every Agent Card (`demo-mcp`'s public `/mcp`+`/mcp/native` are a labelled, deliberately non-conformant interop demo). Optional transports, brokers and workload-identity systems are leaf bindings that depend INWARD; core never depends outward. Ordering that must never invert: `certificate → transport evidence → optional workload binding → application authentication → canonical resolution → Admission` ([ADR-0057](docs/architecture/decisions/0057-a2a-over-https-admission-mtls-optional.md) + [spec 339](specs/339-agentic-primitives-edge-https-admission.md); gate `check:optional-package-boundaries`).
|
|
11
|
+
- **DO-local storage is a SERVING-PLANE concern; the vault is the RECORD.** Durable Object storage
|
|
12
|
+
(`state.storage` / `state.storage.sql`) exists for ordering, cursors, receipts, request-id ledgers,
|
|
13
|
+
projections and caches — things that are derived, rebuildable, and needed at request latency. It MUST
|
|
14
|
+
NOT become the home of durable private records, message bodies, grants, or any self-sovereign data.
|
|
15
|
+
Those live in the owner's vault behind demo-mcp, where per-record delegation scope is enforced
|
|
16
|
+
(ADR-0055). **The drift to refuse is DO-local quietly becoming the person/org vault store** — it
|
|
17
|
+
arrives as a performance argument and leaves you with a second enforcement point, a second authority
|
|
18
|
+
model, and records nobody can carry to another Home. The test for any new DO table: *if this DO were
|
|
19
|
+
wiped, is the loss a rebuild or a bereavement?* A rebuild is fine; a bereavement means it belonged in
|
|
20
|
+
the vault. Applies to `InteractionsDO` and to `PrincipalGatewayDO` alike — the latter constructs a
|
|
21
|
+
DO-SQL vault internally, which is why an app MUST inject the MCP-backed one rather than inherit it
|
|
22
|
+
from a constructor ([ADR-0055](docs/architecture/decisions/0055-vaults-are-the-canonical-system-of-record-for-content.md)).
|
|
23
|
+
- **Define the capability model ONCE; generate the UX behaviour AND the agent surfaces from it.** Never
|
|
24
|
+
build the UX and then separately teach Ask how the application works. The Ask in Home drives everything
|
|
25
|
+
the Home UX drives because both are projections of one semantic definition: the **ontology** (how the
|
|
26
|
+
domain is shaped) + the **capability** (ADR-0051 id + label — what the agent advertises) + the
|
|
27
|
+
**SKILL.md execution contract** (`SkillExecutionContractV1` — risk, requirement type, `resourceArg`/
|
|
28
|
+
`authorityArg`, approvals, evidence, spec-360 `effects`), joined by the `~/skills` compiler into an
|
|
29
|
+
`AgentHarnessDefinitionV1` and consumed — never authored — here. From that one model come the Ask's tool
|
|
30
|
+
set, the published vocabulary, the A2A card skills, each act's mandate requirement and risk floor, what
|
|
31
|
+
follows the act, and the Home affordance. **If adding a feature means writing an invoker AND a vocabulary
|
|
32
|
+
entry AND a UI handler, the model is not doing its job.** The bugs this is made of: a Treasury playbook
|
|
33
|
+
that promised "every payment leaves a receipt" into a field nothing read, so money moved and neither
|
|
34
|
+
party was told; and `skills:includesSkill` compiled from a hand-kept `CAPABILITY_PROFILE` table instead
|
|
35
|
+
of the contract a domain author wrote. **Behaviour is generated; authority never is** — a generated
|
|
36
|
+
surface is consulted by no verifier (spec 354 §1 / ADR-0041). Hand-adding a `CAPABILITY_PROFILE` row is
|
|
37
|
+
the anti-pattern; the fallback warns per capability so it stays visible.
|
|
38
|
+
([agent-rules/one-capability-model-generates-both.md](docs/architecture/agent-rules/one-capability-model-generates-both.md), specs 354/355/359)
|
|
39
|
+
- **Domain relationships live in the ONTOLOGY, not in prompts, SKILL.md files or app tables.** A claim about how the domain is shaped — "a treasury belongs to the agent that holds it", "an inbox belongs to a person", "an organization pays from its own balance" — goes in `packages/ontology/tbox/*.ttl` with a comment saying what it means and what it is NOT, and code binds to it BY IRI (`packages/ontology/src/party-roles.ts`). `pnpm check:ontology-bindings` fails the build when an IRI is not declared in the T-box or is the wrong kind: the code may not invent domain vocabulary. **A string-similarity heuristic standing in for a modelled relationship is the specific failure this prevents** — the Ask once looked for a treasury whose NAME resembled its owner's (`alice` → `alice.treasury`), which the ontology never claimed and the estate does not do, so paying "alice" dead-ended because hers is `alice2.treasury`. A planner prompt is the most tempting place to write a domain rule and the worst place to keep one: prompts say how to BEHAVE, the ontology says how the world is SHAPED, and prompt text should be rendered from the binding ([spec 355](specs/355-ontology-driven-ask-and-orchestration.md) + [agent-rules/ontology-drives-behavior.md](docs/architecture/agent-rules/ontology-drives-behavior.md)).
|
|
40
|
+
- **No third-party multi-sig.** We ship our own; port patterns from Safe (signature packing) but no runtime deps.
|
|
41
|
+
- **Per-package context budget:** `CLAUDE.md` ≤ 60 lines, `AUDIT.md` ≤ 150 lines, `README.md` ≤ 1800 words. If you're tempted to bloat one, fix the package shape.
|
|
42
|
+
- **No `eth_getLogs` in product read paths.** Package and app hot paths use `readContract` only; chain history and human-readable reverse strings come from on-chain storage, an explicit indexer, or app cache — never inline log scans ([ADR-0012](docs/architecture/decisions/0012-no-eth-getlogs-in-product-read-paths.md)). No exceptions: the last walker (`agent-naming` `reverseResolve` log fallback) was removed once spec/222 `reverseResolveString` landed.
|
|
43
|
+
- **No silent fallbacks.** A read/auth path has exactly ONE mechanism. If the canonical path has no answer it returns `null`/empty or throws — it does NOT escalate to a second, different, more expensive mechanism (`try fast catch slow`, empty-result → log walk, strong-check → weak-check). Empty is an answer, not a trigger. Bounded retries of the *same* call and cache-first reads (cache holds the canonical answer) are fine; switching mechanism is not ([ADR-0013](docs/architecture/decisions/0013-no-silent-fallbacks.md)).
|
|
44
|
+
- **The discovery knowledge base holds ONLY public, on-chain-derivable data.** The GraphDB A-box (projected by `demo-discovery-indexer`, read by `demo-discovery-mcp`/`-a2a`) is a world-readable read tier — populate it ONLY with facts any party could reproduce from on-chain state, transactions, or event logs (naming, registry entries, profile, relationships, attestations, custodian/passkey membership from `CustodianAdded`/`PasskeyAdded` logs). NEVER per-viewer state, session/credential secrets, off-chain/relying-app-local data, vault contents, PII, or private linkage ([ADR-0025](docs/architecture/decisions/0025-related-agents-are-private-vault-credentials.md)) — not even hashed, not even temporarily, not even to make a check faster. No secret-derived rows: any opaque form (e.g. custody membership as `sha256(credential‖smartAgent)`) MUST be a public, unsalted, on-chain-*reproducible* function — no server pepper. The indexer is the only writer; consumer services MUST NOT write viewer-/request-derived facts back. The claim: reading the whole KB reveals nothing reading the chain wouldn't ([ADR-0040](docs/architecture/decisions/0040-knowledge-base-only-public-onchain-data.md)).
|
|
45
|
+
- **On the A2A→MCP hop, Web3 is the authority; MCP OAuth is only the ingress envelope.** When an A2A service agent calls an MCP service, the authorization decision is ALWAYS the Web3 chain — on-chain delegation + signature validation (ERC-1271/6492/ECDSA/WebAuthn via `UniversalSignatureValidator`) + entitlement + tool policy (+ key-release grant + audit for data) — re-run server-side against the principal SA. We ship MCP's OAuth (RFC 9728 bearer / protected-resource metadata) as a generic ingress *adapter* so external MCP clients interoperate, but the bearer is at most a transport envelope: it carries only `ap_principal` + a `ap_grant_ref`/`ap_grant_hash` to an encrypted grant bundle, NEVER delegation/entitlement payload or field-level authority. Do NOT let OAuth drift inward: field/record access is never encoded in scopes (`vault:read` ≠ "decrypt all PII"), OAuth claims (`scope`/`aud`/`client_id`) are never trusted *as* the grant, an inbound MCP token is never reused for the next hop (fresh token / RFC 8693 exchange), and the Web3 gates are never skipped because "OAuth already authenticated the caller." For internal A2A→MCP hops the public OAuth ingress may be absent/skipped (the A2A bridge handles MCP tokens server-side, never exposing them downstream) — the delegation+signature+entitlement+policy gates are not. One mechanism (ADR-0013): OAuth-fail rejects, it does not fall back to a weaker token-trust path. When the standard and the substrate conflict, the substrate wins. ERC-4337 SA-bound, signature-verified, on-chain-revocable delegation is our differentiator from OAuth — never trade it for an opaque issuer-granted scope ([ADR-0041](docs/architecture/decisions/0041-web3-authority-not-oauth-on-a2a-to-mcp.md) + [spec 277](specs/277-mcp-delegated-vault-authorization.md) + [spec 269](specs/269-async-delegation-authorized-a2a.md) + [spec 270](specs/270-del-001-activation.md)).
|
|
46
|
+
- **Agentic architecture: first-party web expresses INTENTS to an A2A agent; it NEVER drives MCP directly.** A2A is the orchestration / intelligence / discovery layer — it discovers which capability satisfies a declarative goal, plans (planner/LLM/LangChain), assembles authority (delegation/entitlement), and composes MCP tools AS PRIMITIVES. A first-party UI (demo-web-pro, demo-sso-next) says "rebalance the treasury" / "read Alice's PII for purpose X" — a goal, not a tool name, payload, or call sequence — and posts it to an a2a agent. **`web → (edge) → MCP /tools|/mcp|/mcp/native` directly is the anti-pattern (RPC with extra steps) for first-party apps.** The public MCP ingress (`/mcp`, `/mcp/native`) is interop for EXTERNAL agents/clients only; the Act6 OAuth/Native panels are external-MCP-client *demonstrations of a primitive*, NOT a product-UX model (keep them labelled as such). **Orchestration is a Ring-0 primitive, not external** — the agentic loop (intent→plan→compose→observe→re-plan), the planner/reasoner PORT, and the tool/skill model live in a pure `packages/*` orchestration core; ONLY the concrete LLM-framework binding (LangChain/OpenAI/Anthropic SDK) lives in a dedicated in-repo adapter behind the port — the `chain-state` + `chain-state-viem` pattern (ADR-0037 exiles protocol-conformance + deployed-infra, NOT libraries). `service-agent` stays the deterministic compile/run target; the Ring-0 orchestration core PRODUCES its plans. Apps wire model creds/config; they don't re-implement the loop. The edge is orthogonal: it admits, it does not orchestrate (ADR-0043). Honest tension: the first-party web↔a2a surface is RPC-shaped TODAY (`/session/*`, `/account/*`, `/mcp/person/pii` — verbs); the intent layer (A2A messages/tasks against `A2aTaskDO` + `service-agent` plans) is the objective — build NEW first-party surfaces intent-first; the relayer verbs are legacy. Enforced by review + `check:no-direct-mcp-in-web` (browser-app source; allowlist the labelled external-client demo panels) ([ADR-0044](docs/architecture/decisions/0044-a2a-is-the-agentic-orchestration-layer.md) + [docs/architecture/principles.md](docs/architecture/principles.md)).
|
|
47
|
+
- **Packages are GENERIC trust building blocks; white-label / vertical / deployment code lives in apps.** `packages/*` are reusable + vertical-agnostic. They MUST NOT contain (a) **branding / vertical content** — faith vocabulary (ministry, congregation, discipleship, parish, denomination, gospel, scripture — **`church`/`circle` are the one carve-out: Ring-0 derived AGENT TYPES since 2026-08-30, ADR-0061 amendment; the KIND is generic, the content is not**), white-label/product names, marketing copy, themes; (b) **vertical-specific flows** — faith onboarding steps, impact-portal features; (c) **deployment specifics** — concrete hostnames (`impact-agent.me`/`.io`), the `demo.agent` subregistry, hosting providers (`*.pages.dev`/`*.workers.dev`/`vercel`), host/subdomain parsing. All of that is the **app** layer's job, supplied as an **app-level white-label config object** (spec 234) the generic core consumes. The `.agent` TLD itself is the naming protocol (owned by `agent-naming`) and is allowed. Enforced by `pnpm check:no-domain-in-packages` (hostnames + `demo.agent` + faith vocab) + `check:forbidden-terms`; the rest is doctrine ([ADR-0021](docs/architecture/decisions/0021-generic-packages-vs-white-label-apps.md)). Each app centralizes its domain/white-label literals in one module (e.g. `src/lib/domain.ts`), never scattered.
|
|
48
|
+
|
|
49
|
+
- **Many-registries hypothesis: be what registries are built FROM, not a tenant of one.** Hundreds of (mostly vertical) agent registries will exist; no horizontal winner. Ring 0 ships a generic **registry kit** — SA-anchored registry contracts with pluggable membership/validation hooks, discovery/registry SDK, signed agent-card + binding-proof standards — drawing concepts from ERC-8004/HCS as design inputs, never conformance targets. Specific registries (vertical or 8004/ANS/HCS bridges) are external consumers of the kit ([ADR-0038](docs/architecture/decisions/0038-many-registries-hypothesis-registry-building-primitives.md)).
|
|
50
|
+
- **This repo is Ring 0: primitives only. Integration + product-UX layers live in EXTERNAL repos.** Code that speaks someone else's protocol (ERC-8004 sync, ANS/DNS, HCS/UAID, OASF, Veramo) or runs as deployed infra (indexer, discovery API, explorer) belongs in sibling repos (`agentic-trust`, `agent-indexer`, `agent-explorer`, `oasf`) that import `@agenticprimitives/*` — never the reverse. Ring 0's job is expressive extension points (SA-signed cards, attestation schemas, complete events), not bridges. Learn from ERC-8004/HCS/sibling repos; port *primitive* patterns in, route *conformance* code out ([ADR-0037](docs/architecture/decisions/0037-primitives-pure-repo-external-integration-and-ux-layers.md)).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# One capability model. Generate the UX and the agent surfaces from it — never teach them separately
|
|
2
|
+
|
|
3
|
+
**Canonical rule — all agents.** Specs: [354](../../../specs/354-archetype-driven-agent-behavior.md)
|
|
4
|
+
(playbooks), [355](../../../specs/355-ontology-driven-ask-and-orchestration.md) (ontology drives
|
|
5
|
+
behaviour), [352](../../../specs/352-ask-capability-program.md)/[353](../../../specs/353-app-scoped-ask.md)
|
|
6
|
+
(the Ask). Gates: `pnpm check:agent-skills`, `pnpm check:ontology-bindings`.
|
|
7
|
+
|
|
8
|
+
## The rule
|
|
9
|
+
|
|
10
|
+
> **Define the application's capability model ONCE. Generate both the UX behaviour and the agent
|
|
11
|
+
> skill/discovery surfaces from that same semantic definition.**
|
|
12
|
+
>
|
|
13
|
+
> **Never build the UX first and then separately teach Ask how the application works.**
|
|
14
|
+
|
|
15
|
+
The Ask in Home must be able to drive **everything the Home UX can drive** — and that equivalence is not
|
|
16
|
+
achieved by writing the feature twice. It is achieved because both are projections of one model:
|
|
17
|
+
|
|
18
|
+
| Layer | What it is | Who owns it |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| **Ontology** (`packages/ontology/tbox/*.ttl`) | how the domain is SHAPED — the classes, the relations, the record bindings | this repo, bound by IRI |
|
|
21
|
+
| **Capability** (ADR-0051 id + label) | what the agent ADVERTISES it can do — the branding/discovery half | the domain's Build → Capabilities |
|
|
22
|
+
| **SKILL.md execution contract** (`SkillExecutionContractV1`) | HOW it runs — risk, requirement type, which arg carries the resource, which arg names whose authority, approvals policy, evidence, effects | the domain's Build → Skills |
|
|
23
|
+
| **Compiled definition** (`AgentHarnessDefinitionV1`) | the two joined, by digest | `~/skills` compiler; consumed, never authored, by this repo |
|
|
24
|
+
|
|
25
|
+
From that one model come **all** of:
|
|
26
|
+
|
|
27
|
+
- the **Ask's tool set** (`scopedActionTools` ∩ the playbook's capability ids),
|
|
28
|
+
- the **published vocabulary** a surface builds its scope from (`/harness/vocabulary?agent=`),
|
|
29
|
+
- the **A2A agent card** skills and the discovery projection,
|
|
30
|
+
- the **mandate requirement** each act asks for, and the **risk floor** it cannot go under,
|
|
31
|
+
- **what follows the act** (spec 360 declared effects — the receipt, the notification),
|
|
32
|
+
- and the **UX affordance** in Home.
|
|
33
|
+
|
|
34
|
+
A button and an ask that do the same thing must be reading the same declaration. If adding a feature
|
|
35
|
+
means writing an invoker *and then* adding it to a vocabulary list *and then* adding a UI handler, the
|
|
36
|
+
model is not doing its job — that triplication is the smell this rule names.
|
|
37
|
+
|
|
38
|
+
## Why — the bugs this rule is made of
|
|
39
|
+
|
|
40
|
+
**Money moved and nobody was told.** The Treasury playbook said *"every payment leaves a receipt"* in its
|
|
41
|
+
`evidence:` frontmatter. That compiled into `evidenceRequirements` — and **nothing read that field.** The
|
|
42
|
+
UX had been built (a payment happens) and the declaration was decoration. A person paid another person
|
|
43
|
+
and neither got so much as a message. (Spec 360 §0.)
|
|
44
|
+
|
|
45
|
+
**The archetype's skills drove nothing.** `skills:includesSkill` was read into `ArchetypeFacts.skills`,
|
|
46
|
+
and `compileArchetype` never referenced it — every runtime shape came from `CAPABILITY_PROFILE`, a
|
|
47
|
+
hand-maintained TypeScript table. The Build → Skills area let a domain author write a contract that no
|
|
48
|
+
running agent would ever consult.
|
|
49
|
+
|
|
50
|
+
**The cost is quadratic and it compounds.** Membership took a day of hand-built readers, parsers and
|
|
51
|
+
gates; the payment fan-out took a week. The Home spans nine domains. At the artisanal rate — build the
|
|
52
|
+
UX, then teach the Ask, then teach discovery — covering them is quarters of work that competitors ship
|
|
53
|
+
as configuration (spec 359 §0).
|
|
54
|
+
|
|
55
|
+
## What it looks like when followed
|
|
56
|
+
|
|
57
|
+
Adding *"notify the payee on transfer"* is an **`effects:` block in a SKILL.md** — not an invoker change,
|
|
58
|
+
not a UI handler, not a new Ask verb. The compiler carries it into the definition, the harness discharges
|
|
59
|
+
it, and the receipt appears in both parties' vaults and their thread.
|
|
60
|
+
|
|
61
|
+
Reassigning a treasury agent to a read-only Bookkeeper archetype removes payments from **both** the Ask's
|
|
62
|
+
offers and its published vocabulary, with **no code change** and no change to any authority gate.
|
|
63
|
+
|
|
64
|
+
## The line this rule does NOT cross
|
|
65
|
+
|
|
66
|
+
**Behaviour is generated; authority never is.** A capability model says what an agent KNOWS HOW to do and
|
|
67
|
+
what a surface may OFFER. It grants nothing. The mandate, its caveats, and the on-chain enforcers decide
|
|
68
|
+
what MAY happen, and no generated surface is consulted by a verifier (spec 354 §1, spec 353 §4,
|
|
69
|
+
ADR-0041). Generating the UX and the agent surfaces from one model makes them *honest about the same
|
|
70
|
+
things* — it must never make either of them *authoritative*.
|
|
71
|
+
|
|
72
|
+
## How to comply
|
|
73
|
+
|
|
74
|
+
1. Model the domain fact in the T-box; bind it by IRI (`ontology-drives-behavior.md`).
|
|
75
|
+
2. Declare the capability (id + label) in the domain — the advertisement.
|
|
76
|
+
3. Write the SKILL.md execution contract for the skill that realises it — risk, requirement, args,
|
|
77
|
+
approvals, evidence, effects. Link it to the archetype (`skills:includesSkill`).
|
|
78
|
+
4. Let the compiler produce the definition. **Do not** hand-add a row to `CAPABILITY_PROFILE`; that table
|
|
79
|
+
is a fallback for capabilities no skill defines yet, and every use of it emits a warning naming the
|
|
80
|
+
capability, so the remaining hand-kept rows stay visible rather than quietly permanent.
|
|
81
|
+
5. Consume the definition in the Ask, the vocabulary, the card and the UX. If a surface needs something
|
|
82
|
+
the definition cannot express, **extend the contract schema** — do not special-case the surface.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Domain relationships live in the ontology, not in prompts or tables
|
|
2
|
+
|
|
3
|
+
**Canonical rule — all agents.** Cursor mirror: `.cursor/rules/ontology-drives-behavior.mdc`.
|
|
4
|
+
Spec: [355](../../../specs/355-ontology-driven-ask-and-orchestration.md). Gate: `pnpm check:ontology-bindings`.
|
|
5
|
+
|
|
6
|
+
## The rule
|
|
7
|
+
|
|
8
|
+
**A claim about how the domain is shaped belongs in `packages/ontology/tbox/*.ttl`, bound by IRI from
|
|
9
|
+
code. It does not belong in a prompt, a SKILL.md, a lookup table in an app, or an inline heuristic.**
|
|
10
|
+
|
|
11
|
+
If behaviour depends on a fact like *"a treasury belongs to the agent that holds it"*, *"an inbox belongs
|
|
12
|
+
to a person"*, or *"an organization pays from its own balance"* — that fact is ontology. Name the term,
|
|
13
|
+
say in its `rdfs:comment` what it means and what it does NOT mean, then bind to its IRI.
|
|
14
|
+
|
|
15
|
+
## Why — the bug this rule is made of
|
|
16
|
+
|
|
17
|
+
The Ask had to turn *"send alice 20 USDC"* into an address. It used a rule nobody had written down: **look
|
|
18
|
+
for a treasury whose NAME resembles the person's** — `alice` → `alice.treasury`.
|
|
19
|
+
|
|
20
|
+
Nothing in the ontology said that. Nothing in the estate did it either: Alice's treasury is called
|
|
21
|
+
`alice2.treasury`. The guess found nothing, and a payment that should have worked dead-ended with a
|
|
22
|
+
question the person could not answer.
|
|
23
|
+
|
|
24
|
+
The real relationship — *the treasury is chartered under the person who holds it* — existed as a `parent`
|
|
25
|
+
field in one app's records, a suffix table in another, and a sentence in a prompt. Three encodings of one
|
|
26
|
+
fact, none of them checkable against each other, and the one the resolver used was wrong.
|
|
27
|
+
|
|
28
|
+
**A string-similarity heuristic standing in for a modelled relationship is the specific failure this rule
|
|
29
|
+
exists to prevent.** It is seductive because it works in the demo data and it is unfalsifiable in code.
|
|
30
|
+
|
|
31
|
+
## How to comply
|
|
32
|
+
|
|
33
|
+
1. **Model it.** Add the class or property to the T-box with a comment that says what it means, and — for
|
|
34
|
+
anything near authority — what it is *not*. `ap:charteredUnder` says plainly: never authority; the
|
|
35
|
+
child holds its own custody; a resolver may follow it, a verifier may not read it as permission.
|
|
36
|
+
2. **Bind by IRI.** Reference it from `packages/ontology/src/party-roles.ts` (or the equivalent binding
|
|
37
|
+
for your concern), never by re-describing it.
|
|
38
|
+
3. **Let the gate hold you to it.** `check:ontology-bindings` fails the build when an IRI is not declared
|
|
39
|
+
in the T-box, or is declared as the wrong kind. The code may not invent domain vocabulary.
|
|
40
|
+
4. **Mirror it.** The `~/skills` `agentic-trust` T-box grounds on AP terms; add the `at:` counterpart with
|
|
41
|
+
`rdfs:seeAlso` and push it to GraphDB (`scripts/sync_ontology.py push`).
|
|
42
|
+
|
|
43
|
+
## Applies especially to LLM-facing surfaces
|
|
44
|
+
|
|
45
|
+
A planner prompt is the most tempting place to write a domain rule and the worst place to keep one: it is
|
|
46
|
+
invisible to every gate, it drifts silently, and a model will apply it confidently to cases it was never
|
|
47
|
+
meant for. **Prompts say how to behave; the ontology says how the world is shaped.** When a prompt needs a
|
|
48
|
+
domain fact, it should be rendered FROM the ontology binding (as the Ask's party words and type
|
|
49
|
+
preferences now are), not written into the prose.
|
|
50
|
+
|
|
51
|
+
## Drift triggers — STOP
|
|
52
|
+
|
|
53
|
+
- *"Just check if the name starts with…"* — **HARD STOP.** That is a relationship. Model it.
|
|
54
|
+
- *"Add it to the system prompt so the planner knows"* — **STOP.** Add it to the T-box and render the
|
|
55
|
+
prompt from the binding.
|
|
56
|
+
- *"A table in this app is simpler than a new ontology term"* — it is, once. It is a second opinion about
|
|
57
|
+
the domain from then on, and this rule exists because one of those was wrong in production.
|
|
58
|
+
- *"The ontology doesn't have a term for this"* — then the term is missing. Adding it IS the work.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Service-agent A2A signing: the key is a DELEGATE, never the identity
|
|
2
|
+
|
|
3
|
+
**Applies to every service that signs A2A messages as an agent identity** — in this repo and in every
|
|
4
|
+
derived repo (`verifiable-content-demo`, `skills`, and any future one).
|
|
5
|
+
|
|
6
|
+
## One sentence
|
|
7
|
+
|
|
8
|
+
A relying service NEVER custodies the agent identity it acts as: the identity is custodied outside
|
|
9
|
+
the service, a **ceremony** mints a narrow **wire** (`identity SA → the service's KMS session key`),
|
|
10
|
+
and the service signs with the session key and presents the wire — so possession of the service's
|
|
11
|
+
credentials can never be possession of the identity.
|
|
12
|
+
→ ADR-0019 · [spec 329 §3.1](../../../specs/329-consultability-and-routing.md) · `apps/demo-a2a/src/session-wire.ts`
|
|
13
|
+
|
|
14
|
+
## Why this keeps getting re-derived (read this part)
|
|
15
|
+
|
|
16
|
+
The existing rail — `pnpm check:no-app-private-keys` — stops at *"use KMS, don't hold a raw key."*
|
|
17
|
+
**That check passes for the wrong architecture.** An app can use KMS correctly, hold no raw key, and
|
|
18
|
+
still be the custodian: if the KMS key IS the identity's key, whoever holds the service-account
|
|
19
|
+
credential can sign anything as that identity, forever, with nothing to revoke.
|
|
20
|
+
|
|
21
|
+
"No held key" is not the property. **"Not the custodian" is the property.** They are not the same
|
|
22
|
+
sentence, and every time only the first one is written down, the second is re-derived — usually
|
|
23
|
+
wrongly, and usually with passing tests.
|
|
24
|
+
|
|
25
|
+
## The pattern
|
|
26
|
+
|
|
27
|
+
1. **The identity is custodied outside the service.** A named account (`<name>.impact`) whose
|
|
28
|
+
custodian is a person or org, reachable through a Home ceremony. Not the relying app.
|
|
29
|
+
2. **A ceremony mints the wire.** The custodian signs `identity SA → the service's KMS session key`:
|
|
30
|
+
`allowedMethods` = **exactly one** skill selector (never `A2A_ANY_SKILL`), `allowedTargets` pinned,
|
|
31
|
+
timestamp-bounded. The service stores the wire; it is a credential, not a secret.
|
|
32
|
+
3. **The service signs with the session key and wraps.** `wrapSessionSignature(wire, sig)` →
|
|
33
|
+
the `0x51` session-wrapped form. The KMS key signs each digest on demand; no raw key at rest.
|
|
34
|
+
4. **The gate verifies per message**, fail-closed: delegator === the claimed signer, wire live,
|
|
35
|
+
one selector matching the requested skill, ECDSA recovers to the wire's delegate, wire
|
|
36
|
+
ERC-1271-valid against the delegator, wire **unrevoked on-chain**.
|
|
37
|
+
|
|
38
|
+
## What this buys, stated as the difference
|
|
39
|
+
|
|
40
|
+
| | key IS the identity | key is a DELEGATE (this rule) |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| custody | the relying service | the named account's custodian, outside it |
|
|
43
|
+
| compromise | you are that agent, for anything, forever | a delegate, only where a wire says so |
|
|
44
|
+
| revocation | re-mint the identity + every grant naming it | the custodian revokes one wire |
|
|
45
|
+
| audit | "the service signed" | "this session key signed under this wire" |
|
|
46
|
+
|
|
47
|
+
## What it does NOT buy — say this out loud
|
|
48
|
+
|
|
49
|
+
**The GCP service-account secret does not disappear.** `demo-bible-mcp` and `impact-a2a` both carry
|
|
50
|
+
`GCP_SERVICE_ACCOUNT_JSON` as a wrangler secret, and that is correct. The secret still exists; what
|
|
51
|
+
changes is what it controls — a revocable delegate instead of an identity. Anyone who expects this
|
|
52
|
+
pattern to remove the secret has misread it and will "fix" it back the wrong way.
|
|
53
|
+
|
|
54
|
+
## Two implementations to copy, not reinvent
|
|
55
|
+
|
|
56
|
+
- **`verifiable-content-demo`, the content-signer ceremony.** The issuer SA is custodied by its owner;
|
|
57
|
+
the ceremony signs a leaf `issuer SA → KMS key address`; the corpus service stores it and signs with
|
|
58
|
+
the key, and a verifier "roots trust in the SA (ERC-1271) while the signer is the HSM key"
|
|
59
|
+
(`apps/demo-bible-a2a/src/index.ts`).
|
|
60
|
+
- **`demo-a2a`, the session wire.** An org's DO holds no org key; `signAsOrg` signs with the
|
|
61
|
+
interactions-session KMS key and wraps with the steward-minted wire (`src/session-wire.ts`).
|
|
62
|
+
|
|
63
|
+
`session-wire.ts` is **skill-agnostic** — pass the skill to pin, never hardcode one. It was
|
|
64
|
+
consult-only for a while, and that is exactly what forced the next service toward custodying its own
|
|
65
|
+
identity because "there was no other path".
|
|
66
|
+
|
|
67
|
+
## Rules
|
|
68
|
+
|
|
69
|
+
1. **Never configure a KMS key as the service's own agent identity.** If a service signs as
|
|
70
|
+
`X_AGENT_SA`, the key it signs with must be a delegate of `X_AGENT_SA`, not `X_AGENT_SA` itself.
|
|
71
|
+
2. **Signing an A2A message as an identity requires a wire.** `hashA2aMessage(...)` + a bare signer,
|
|
72
|
+
with no wire presented, is the violation. Reviewers: grep for that pair.
|
|
73
|
+
3. **One selector per wire, never `A2A_ANY_SKILL`.** A wire is per-rail. Pin the skill the request
|
|
74
|
+
names wherever the request names one.
|
|
75
|
+
4. **The wire is stored, the identity is not.** A service may hold wires (they are revocable and
|
|
76
|
+
narrow). It may never hold anything that grants the identity itself.
|
|
77
|
+
5. **No new signing path without naming its custodian.** If you cannot say who custodies the identity
|
|
78
|
+
and how they revoke, the design is not finished.
|
|
79
|
+
|
|
80
|
+
## Before writing any of it
|
|
81
|
+
|
|
82
|
+
`ls ~/agenticprimitives/packages` and grep the two implementations above. Both problems — the
|
|
83
|
+
ceremony and the wrapped signature — are solved; the cost of checking is two greps, and the cost of
|
|
84
|
+
skipping it has twice been an identity that had to be withdrawn.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Projection metadata per rule in the corpus (399 §3.4): the .mdc frontmatter Cursor reads, and whether the rule has a Cursor mirror today. Bodies come from the source; this file never carries rule text.",
|
|
3
|
+
"coordination-vs-orchestration": { "description": "Coordination (between agents, discovery, capability level) vs orchestration (within agent, SKILL.md + tools)", "alwaysApply": true, "cursor": true },
|
|
4
|
+
"demo-person-org-distinct": { "description": "A demo person SA is never also their organization SA (ADR-0046)", "alwaysApply": true, "cursor": true },
|
|
5
|
+
"external-registry-integrations": { "description": "Keep external registry integrations out of primitive packages", "alwaysApply": true, "cursor": true },
|
|
6
|
+
"ontology-drives-behavior": { "description": "Domain relationships live in the ontology T-box, not in prompts or app tables (spec 355)", "alwaysApply": true, "cursor": true },
|
|
7
|
+
"service-agent-signing": { "description": "Service agents sign with a DELEGATE key under a ceremony-minted wire — never custody the identity they act as (ADR-0019)", "alwaysApply": true, "cursor": true },
|
|
8
|
+
"skill-terminology": { "description": "Capability as canonical noun; qualify every standards-specific \"skill\" (ADR-0050, ADR-0051)", "globs": ["**/*"], "alwaysApply": true, "cursor": true },
|
|
9
|
+
"vault-and-ontology": { "description": "Prefer vault-backed authority and standards-based ontology patterns", "alwaysApply": true, "cursor": true },
|
|
10
|
+
"one-capability-model-generates-both": { "description": "Define the capability model once; UX behaviour and agent surfaces are generated from it (specs 354/355/359)", "alwaysApply": true, "cursor": false },
|
|
11
|
+
"vault-records-are-ontology-shaped": { "description": "Vault records are ontology-shaped: record keys bind to T-box classes by IRI", "alwaysApply": true, "cursor": false },
|
|
12
|
+
"hard-rules": { "description": "The root CLAUDE.md hard rules (canonical identifier, no silent fallbacks, generic packages, web → A2A, vault is the record)", "alwaysApply": true, "cursor": false }
|
|
13
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Capability terminology (ADR-0050 + ADR-0051)
|
|
2
|
+
|
|
3
|
+
**Capability** is the stable semantic noun. Bare **skill** is ambiguous — always qualify.
|
|
4
|
+
|
|
5
|
+
## One sentence
|
|
6
|
+
|
|
7
|
+
OASF skills **classify** capabilities; A2A AgentSkills **advertise** capabilities; Agent Skill packages **implement** procedural knowledge for capabilities.
|
|
8
|
+
|
|
9
|
+
## Layer map
|
|
10
|
+
|
|
11
|
+
| Layer | Term | Standards object |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Canonical | **CapabilityDefinition** | AP-owned semantic outcome |
|
|
14
|
+
| Classification | **OASF skill taxon** | OASF record taxonomy |
|
|
15
|
+
| Advertisement | **A2A AgentSkill** / **A2A advertised capability** | Agent Card `skills[]` |
|
|
16
|
+
| Playbook | **Agent Skill package** / **SKILL.md package** | Agent Skills standard |
|
|
17
|
+
| Operation | **MCP tool** | Typed executable invocation |
|
|
18
|
+
| Credential | **capability claim** / **SkillClaimCredential** | Private VC (spec 251) |
|
|
19
|
+
|
|
20
|
+
## Packages
|
|
21
|
+
|
|
22
|
+
| Package | Meaning |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `@agenticprimitives/capability-claims` | Capability claim VCs + vocabulary types |
|
|
25
|
+
| `@agenticprimitives/agent-skills` | **Deprecated alias** — re-exports `capability-claims` |
|
|
26
|
+
|
|
27
|
+
`@agenticprimitives/capability-claims` is **not** Agent Skill packages (`SKILL.md`).
|
|
28
|
+
|
|
29
|
+
## Ontology
|
|
30
|
+
|
|
31
|
+
| Namespace | Canonical | Deprecated alias |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `aps:` | `CapabilityDefinition`, `CapabilityGrounding` | `AgentSkillDefinition`, `SkillGrounding` |
|
|
34
|
+
| `apguide:` | `AgentSkillPackage` | `AgentGuidanceSkill` |
|
|
35
|
+
|
|
36
|
+
## Style rule
|
|
37
|
+
|
|
38
|
+
Outside code implementing a particular standard's types, **skill must have a qualifier**.
|
|
39
|
+
|
|
40
|
+
| Avoid | Use |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| register the skill | register the **OASF skill taxon** / publish the **A2A AgentSkill** |
|
|
43
|
+
| load the skill | load the **Agent Skill package** |
|
|
44
|
+
| verify the skill | verify the **capability claim credential** |
|
|
45
|
+
|
|
46
|
+
## UI
|
|
47
|
+
|
|
48
|
+
Prefer **Capabilities** with sub-sections: advertised, classifications, runtime playbooks, operations, credentials.
|
|
49
|
+
|
|
50
|
+
**Source:** ADR-0051, vocabulary-map.md, spec 325 §2.2 (see agent-rules/README.md).
|
|
51
|
+
|
|
52
|
+
## The advertisement layer is CAPABILITY (ADR-0051, 2026-09-01)
|
|
53
|
+
|
|
54
|
+
Three names. One meaning each. They are not interchangeable and the collision between them is the whole
|
|
55
|
+
reason this rule exists.
|
|
56
|
+
|
|
57
|
+
| Name | What it is | Where the word `skills` is allowed |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| **Capability** (`AdvertisedCapabilityV1`) | What the agent CAN DO. The word in the profile, the vault, on chain, in discovery, in every UI. | — |
|
|
60
|
+
| **Protocol features** (`ProtocolFeatureSetV1`) | A2A's streaming / push / extensions flags. The wire calls this object `capabilities`, which is the collision. | — |
|
|
61
|
+
| **Agent Skill package / playbook** | SKILL.md procedural guidance for a harness. | — |
|
|
62
|
+
|
|
63
|
+
`skill` without a qualifier means **playbook only**. `skills[]` is A2A's name for capabilities and is
|
|
64
|
+
legitimate in exactly two places: `@agenticprimitives/agent-profile/a2a` (the wire types and the projector
|
|
65
|
+
that writes them) and a raw-JSON inspector showing a steward the literal document. Anywhere else it is a
|
|
66
|
+
defect. Enforced by `pnpm check:capability-vocabulary`.
|
|
67
|
+
|
|
68
|
+
**The system of record is `profile.capabilities[]`.** The A2A card's `skills[]` and ARD's `capabilities[]`
|
|
69
|
+
are projections of it; the A2A projector is their only writer. On chain the record is `atl:capabilities`
|
|
70
|
+
(ids only — the chain proves WHICH capabilities are claimed, not their prose). `atl:skills` is that same
|
|
71
|
+
rail under its old name: read when the new one is empty, never written. That is a documented migration,
|
|
72
|
+
not a fallback (ADR-0013) — same contract, same owner, same value, only the key moved.
|
|
73
|
+
|
|
74
|
+
What this replaced, and why it mattered: the card editor had a section titled **Capabilities** holding
|
|
75
|
+
protocol flags, next to one titled **Skills** holding what the agent can do. Backwards twice over, in the
|
|
76
|
+
one screen where a steward decides what the world sees.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Vault Storage And Ontology
|
|
2
|
+
|
|
3
|
+
- Do not introduce app-local persistent storage for durable authority state when a vault-backed record or Agentic Primitives rail should own it. This includes membership, invitations, roles, reports, relationships, attestations, discussions, messages, onboarding state, and access decisions.
|
|
4
|
+
- Treat D1, KV, R2, Durable Objects, local JSON, and app-specific ledgers as caches, indexes, queues, or runtime infrastructure only. Durable domain truth belongs in person/org/service vault records, Home/a2a rails, MCP vault records, chain state, or package-owned canonical stores.
|
|
5
|
+
- Model onboarding and membership with standards-aligned situations: pending invite/request situation first, then approved/accepted membership or role situation. Approval writes canonical relationships, delegations, entitlements, rosters, attestations, or vault records.
|
|
6
|
+
- Smart Agent address is the canonical identifier. Agent names, email addresses, display names, handles, registry entries, and UI labels are facets used for lookup/contact, never canonical membership identity.
|
|
7
|
+
- Prefer existing specs, ADRs, package ontologies, PROV/VC/delegation/relationship semantics, and Agentic Primitives vocabulary before creating new fields or classes.
|
|
8
|
+
- Do not create one-off app hacks that bypass ontology relationships, vault authorization, delegation verification, package boundaries, or a2a/mcp data routes. If a needed class/property/pattern is missing, update the spec/ontology first and implement against that standard.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Vault records are ontology-shaped, so a question can read them without interpreting them
|
|
2
|
+
|
|
3
|
+
**Canonical rule — all agents.** Spec: [356 §2.1](../../../specs/356-ontology-grounded-vault-questions.md).
|
|
4
|
+
Gate: `pnpm check:ontology-bindings`. Ledger: `npx tsx scripts/vault-ontology-coverage.mts`.
|
|
5
|
+
Sibling rule: [ontology-drives-behavior](ontology-drives-behavior.md) — that one is about BEHAVIOUR;
|
|
6
|
+
this one is about the DATA the behaviour reads.
|
|
7
|
+
|
|
8
|
+
## The rule
|
|
9
|
+
|
|
10
|
+
**A record an agent stores should be readable BY the ontology, not interpreted INTO it.** Two halves:
|
|
11
|
+
|
|
12
|
+
1. **The KEY binds to a class.** `org.membership:member:<sa>` IS an `aporg:OrganizationMembership`.
|
|
13
|
+
Declared in `packages/ontology/src/vault-records.ts`, checked against the T-box at build.
|
|
14
|
+
2. **The FIELDS are the properties.** `memberAgent`, `organizationAgent`, `hasRoleAssignment` — the same
|
|
15
|
+
names the T-box uses, declared in the binding's `properties` map and checked the same way.
|
|
16
|
+
|
|
17
|
+
When both hold, "who are the members" is a lookup. When they do not, something has to know that `orgAgent`
|
|
18
|
+
means the member here and the organization there — and that something is a person reading code, or a model
|
|
19
|
+
guessing. Both were happening.
|
|
20
|
+
|
|
21
|
+
## Entity or index — say which
|
|
22
|
+
|
|
23
|
+
A binding carries a `role`:
|
|
24
|
+
|
|
25
|
+
- `entity` (default) — a THING the ontology describes. A question may be answered from it.
|
|
26
|
+
- `index` — a projection over entities: an index, a cursor, a name map, an event log. **Never askable.**
|
|
27
|
+
A question answered from an index is a question answered from a cache, and the cache is what goes
|
|
28
|
+
stale (ADR-0055).
|
|
29
|
+
|
|
30
|
+
A record type in neither list is UNCLASSIFIED — a gap, not a decision. The ledger prints those.
|
|
31
|
+
|
|
32
|
+
## What this prevents, concretely
|
|
33
|
+
|
|
34
|
+
- **Membership existed in the T-box and nowhere in a vault** (finding ORG-MEM-1). Invitations were being
|
|
35
|
+
read as membership, which the T-box explicitly forbids — an invitation is an *enrollment instrument*,
|
|
36
|
+
and satisfying one creates no membership. The only complete roster was a Home KV cache of a record
|
|
37
|
+
nothing ever wrote.
|
|
38
|
+
- **A reader guessed a shape and was believed.** `directory.data` is an array; the roster reader asked for
|
|
39
|
+
`doc.listings` and answered "0 members" for every organization, forever. Zero is plausible.
|
|
40
|
+
- **Bindings invented vocabulary.** `apcoord:EndeavorEvent` and `aps:` (for `skill#`) were both wrong and
|
|
41
|
+
both shipped, because the gate did not read record bindings until 2026-09-05.
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- **Never invent a class to make a binding compile.** Add the term to the T-box with the comment saying
|
|
46
|
+
what it means and what it is NOT, or bind to the term that already says it. If neither fits, the record
|
|
47
|
+
stays unclassified and appears in the ledger — that is the honest state.
|
|
48
|
+
- **One parser per record shape.** A shape belongs in one module every reader shares
|
|
49
|
+
(`directory-rows.ts`, `relationship-rows.ts`), because a reader that guesses will guess plausibly.
|
|
50
|
+
- **A binding narrows what a question can MEAN; it never widens what an asker may SEE.** Authority stays
|
|
51
|
+
the delegation, per record, where it always was (spec 277, ADR-0041).
|
|
52
|
+
- **Write the record where the agent that must answer for it can read it.** Membership belongs in the
|
|
53
|
+
organization's own vault — not in a Home cache, and not only in the member's.
|
|
54
|
+
|
|
55
|
+
## The anchor is `~/skills/ontology/agentic-trust`
|
|
56
|
+
|
|
57
|
+
**That ontology is the anchor, and it is kept current.** It is the browsable, cross-domain T-box behind
|
|
58
|
+
GraphDB (`skills-contexts`, served at skills.faithnet.io) that faith, family-office, commerce and the
|
|
59
|
+
rest align to. Its own header states the relationship — *"stay grounded in, never fork"* — and it cites
|
|
60
|
+
this repo's namespaces by `rdfs:seeAlso` wherever it restates one of our ideas in its own shape.
|
|
61
|
+
|
|
62
|
+
**A term that says how the DOMAIN is shaped belongs in both.** Add it here (bound by IRI, checked by
|
|
63
|
+
`check:ontology-bindings`), then reflect it there in that repo's shape with `rdfs:seeAlso` back to our
|
|
64
|
+
IRI, cluster it in `clusters.ttl`, and push:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx tsx scripts/check-skills-ontology-sync.ts # what is here and not there
|
|
68
|
+
cd ~/skills && GRAPHDB_USER=… GRAPHDB_PASSWORD=… python3 scripts/sync_ontology.py push --direct
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Where the line runs.** Not "domain vs implementation" — that reading is too coarse and got the
|
|
72
|
+
registry wrong. **A value two parties must AGREE ON belongs in the anchor; one only our own code passes
|
|
73
|
+
to itself does not.** A registry's binding-proof hash, card digest and lifecycle-log chaining look like
|
|
74
|
+
implementation and are nothing of the sort: a registry is a STANDARDS SURFACE (spec 279 / ADR-0038, the
|
|
75
|
+
ARD and ACP work), and an implementor of the standard has to agree with us on exactly those terms. As of
|
|
76
|
+
2026-09-05 all 946 terms are reflected. The judgement is a person's;
|
|
77
|
+
the report exists to make the list short enough to make it. A concept only the implementation knows is a
|
|
78
|
+
concept the anchor cannot cluster, show, or reason over beside the domain ontologies, which is most of
|
|
79
|
+
the reason the anchor exists.
|
|
80
|
+
|
|
81
|
+
## Where the gaps are
|
|
82
|
+
|
|
83
|
+
`npx tsx scripts/vault-ontology-coverage.mts alice nathan bob` surveys the live estate and prints every
|
|
84
|
+
record family, bound or not. It is a report rather than a build gate: it needs real vaults. Run it after
|
|
85
|
+
adding a record type, and bind what it lists.
|
package/spec.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @agenticprimitives/devkit — package spec
|
|
2
|
+
|
|
3
|
+
Spec of record: [specs/399-repository-split-program.md](../../specs/399-repository-split-program.md) §3.3 (gates as
|
|
4
|
+
CLIs), §3.4 (rules projection), §3.1/§5.2 (`ap upgrade`), and [specs/398-agentic-primitives-ux-strategy.md](../../specs/398-agentic-primitives-ux-strategy.md)
|
|
5
|
+
§10 (the Developer Kit: commands, `agentic.lock.json`, coding-agent projections). Decision: [ADR-0063](../../docs/architecture/decisions/0063-ring-0-is-packages-and-contracts-products-live-in-their-own-repos.md)
|
|
6
|
+
§1 (the kit stays with the packages) and §8 (doctrine travels as data, drift by digest).
|
|
7
|
+
|
|
8
|
+
Owns: the doctor rule registry (399 §2.3 ids), the rule config schema and the Ring-0 reference layout, the rules
|
|
9
|
+
projection and its sidecar, the lock document, the upgrade planner, and the dispatchers. Does not own: any
|
|
10
|
+
conformance suite (`a2a`, `mcp-protocol`), the live-gates runner (`evaluation`), the product-repo template
|
|
11
|
+
(`create-app`), or any rule TEXT (the corpus is synced in from `docs/architecture/agent-rules`).
|