agent-provenance.md
markdown
| 1 | # Agent Provenance in Muse |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | As Muse hosts multiple autonomous agents editing the same repository |
| 6 | concurrently, attribution and audit trail become critical. Agent provenance |
| 7 | answers: **who wrote this commit, with which model, using which toolchain, |
| 8 | and can that attribution be verified cryptographically?** |
| 9 | |
| 10 | ## Commit-level fields |
| 11 | |
| 12 | `CommitRecord` in `muse/core/store.py` carries six new optional fields |
| 13 | (all default to empty string for backward compatibility): |
| 14 | |
| 15 | | Field | Description | |
| 16 | |-------|-------------| |
| 17 | | `agent_id` | Stable identifier for the agent or human (`"counterpoint-bot"`, `"alice"`) | |
| 18 | | `model_id` | AI model version used (`"gpt-5-turbo"`, `"claude-4"`) | |
| 19 | | `toolchain_id` | Agent framework version (`"muse-agent-v2.1"`) | |
| 20 | | `prompt_hash` | SHA-256 of the system prompt (no raw text stored) | |
| 21 | | `signature` | HMAC-SHA256 hex digest of the commit ID under the agent's key | |
| 22 | | `signer_key_id` | Short fingerprint of the signing key (for key lookup) | |
| 23 | |
| 24 | ## Signing and verification |
| 25 | |
| 26 | `muse/core/provenance.py` provides: |
| 27 | |
| 28 | ```python |
| 29 | # Generate a new 32-byte HMAC key. |
| 30 | key = generate_agent_key() |
| 31 | |
| 32 | # Persist the key to .muse/keys/<agent_id>-<fingerprint>.key |
| 33 | write_agent_key(repo_root, agent_id, key) |
| 34 | |
| 35 | # Sign a commit ID. |
| 36 | sig = sign_commit_hmac(commit_id, key) |
| 37 | |
| 38 | # Verify. |
| 39 | ok = verify_commit_hmac(commit_id, sig, key) # uses hmac.compare_digest |
| 40 | |
| 41 | # Convenience: sign a full CommitRecord in one call. |
| 42 | signed_commit = sign_commit_record(repo_root, commit_record) |
| 43 | ``` |
| 44 | |
| 45 | HMAC-SHA256 using Python's standard `hmac` module requires no new |
| 46 | dependencies. If stronger non-repudiation is needed in the future, |
| 47 | the `sign_commit_hmac` function can be upgraded to Ed25519 using the |
| 48 | `cryptography` package without changing any callers. |
| 49 | |
| 50 | ## AgentIdentity |
| 51 | |
| 52 | `make_agent_identity()` constructs an `AgentIdentity` TypedDict: |
| 53 | |
| 54 | ```python |
| 55 | identity = make_agent_identity( |
| 56 | agent_id="counterpoint-bot", |
| 57 | model_id="gpt-5", |
| 58 | toolchain_id="muse-agent-v2", |
| 59 | prompt="system: you are a music composition agent", |
| 60 | execution_context={"env": "ci", "run_id": "1234"}, |
| 61 | ) |
| 62 | ``` |
| 63 | |
| 64 | Sensitive fields (`prompt`, `execution_context`) are hashed before storage — |
| 65 | the raw strings never appear in the identity record. |
| 66 | |
| 67 | ## Key storage |
| 68 | |
| 69 | Agent keys live at `.muse/keys/<agent_id>-<fingerprint>.key` as hex-encoded |
| 70 | files. Multiple agents can coexist; each has its own key file keyed by |
| 71 | `agent_id`. Key files should be added to `.museignore` and managed |
| 72 | separately from the repository content. |
| 73 | |
| 74 | ## Querying provenance |
| 75 | |
| 76 | The music query DSL supports provenance fields directly: |
| 77 | |
| 78 | ```bash |
| 79 | muse music-query "agent_id == 'counterpoint-bot' and note.pitch > 60" |
| 80 | muse music-query "model_id == 'gpt-5'" |
| 81 | ``` |
| 82 | |
| 83 | ## Related files |
| 84 | |
| 85 | | File | Role | |
| 86 | |------|------| |
| 87 | | `muse/core/provenance.py` | `AgentIdentity`, key I/O, HMAC signing | |
| 88 | | `muse/core/store.py` | `CommitRecord`, `CommitDict` — six new fields | |
| 89 | | `tests/test_provenance.py` | Unit tests | |