Skip to content

Latest commit

 

History

History
709 lines (556 loc) · 22.6 KB

File metadata and controls

709 lines (556 loc) · 22.6 KB

Section AgentFS MVP Contract

Purpose

This document answers the Phase 1 readiness questions in AGENTFS_DEVELOPMENT_READINESS.md.

It is the development contract for the first implementation slice:

Agent / Installation / FS / SourceProfile / Grant / Share / Credential / Commit / Event

Hooks, proposal/approval workflows, path-scoped grants, and AGENTS.md enforcement are out of scope for this contract.

正式产品的跨机 / 多 agent sharing 依赖 Section Control Service。服务端是 agent identity、installation、grant、share、source profile、credential 的权威。

1. Terms And Identity

IDs are stable, opaque strings. Display names are metadata and can change.

Term Format Notes
agent_id agt_ + 32 lowercase hex chars Issued by Section Control Service and cached locally
installation_id ins_ + 32 lowercase hex chars One local machine/runtime for an agent
fs_id fs_ + 32 lowercase hex chars Generated at FS creation; independent from source profile name
source_profile_id srcp_ + 32 lowercase hex chars Server-side backing source profile
share_id shr_ + 32 lowercase hex chars Server-side share record
credential_binding_id cred_ + 32 lowercase hex chars Short-lived sync credential audit record
mount_id derived from fs_id + canonical_local_root in MVP One active local root per FS per local installation store
commit_id cmt_ + 32 lowercase hex chars Generated before commit metadata is written
event_id evt_ + 13 digit epoch-ms + _ + 16 lowercase hex chars Event identity only; ordering uses seq
event_seq per-FS integer, starts at 1 Strictly increases inside one FS
grant_id grt_ + 32 lowercase hex chars Generated when a grant is created

All timestamps are Unix epoch milliseconds in UTC stored as integers.

Path strings are UTF-8, slash-separated, source-root-relative paths:

  • no leading slash
  • no .. segments
  • no empty segment except the root path represented as ""
  • .section/** is reserved Section metadata

2. Authority Model

The truth hierarchy is:

Section Control Service = identity, grant, share, source profile, credential authority
accepted commit log = governance truth
backing source = materialized filesystem state
local mount = working copy

A commit is accepted when:

  1. Section Control Service confirms the committing agent has an active commit capability,
  2. the local base commit equals the current shared head,
  3. the commit does not include reserved metadata paths,
  4. the commit record is written under .section/agentfs/commits/,
  5. heads/current.json points to that commit.

The backing source can lag behind the accepted commit log.

If commit metadata succeeds but file materialization fails:

  • the commit remains the governance truth,
  • heads/current.json remains pointed at the accepted commit,
  • commit materialization state becomes failed_to_materialize,
  • FS state is error,
  • further commits are blocked until materialization is repaired.

Watchers can rely on accepted commit records and AgentFS events as governance authority. Service-side events are authoritative for grant/revoke governance. Backing-source events are a mirror and commit/materialization audit surface. File reads rely on the materialized backing source and may lag while FS state is syncing or error.

External edits to the backing source are not governed commits. They are detected by source/path comparison and should surface as drift or conflict, using the existing stale-overwrite protection.

3. Control Metadata Layout

Service-owned records:

agents
installations
source_profiles
grants
shares
credential_bindings
events

AgentFS metadata may be mirrored under the backing source:

.section/
  agentfs/
    fs.json
    heads/
      current.json
    grants/
      <grant_id>.json
    commits/
      <commit_id>.json
    events/
      <event_id>.json

Rules:

  • .section/** is reserved.
  • Normal commits cannot modify reserved metadata paths.
  • The local mount may expose .section/root.json for discovery.
  • The local mount should not expose .section/** as normal user content when it can avoid it.
  • If reserved metadata appears in a working copy, commit apply must ignore it for dirty detection and reject explicit attempts to commit it.

Metadata files are rewritten as whole JSON documents. The MVP does not use JSONL append files because object backends do not provide portable append semantics.

Authority rules:

  • Section Control Service is authoritative for identity, grant, share, source profile, and credential decisions.
  • Section Control Service is authoritative for grant/revoke events.
  • The backing-source metadata namespace is a mirror for materialization, audit, repair, and diagnosis.
  • fs events and watch --agentfs return a merged event view from Section Control Service and backing-source metadata.
  • If grant/revoke mirror writes fail, the service-side governance mutation and service-side event still stand.
  • Normal commits cannot modify mirrored metadata paths.
  • fs list, fs status, fs events, and watch --agentfs only expose FS metadata to agents with read capability.

Metadata Write Lock

AgentFS 用锁记录保护 head 更新和 commit 接受。

新的锁记录写在:

.section/agentfs/locks/head/<lock_token>.json

Lock fields:

{
  "schema_version": 1,
  "fs_id": "fs_...",
  "lock_token": "lck_...",
  "owner_agent_id": "agt_...",
  "created_at_ms": 1780000000000,
  "expires_at_ms": 1780000030000
}

Rules:

  • The lock protects head updates and commit acceptance.
  • Grant, share, source profile, and credential changes are protected by Section Control Service.
  • 申请锁时必须用 backing source 的 create-if-absent / if_not_exists 能力创建自己的锁记录。
  • 同一个 FS 里,未过期锁记录按 created_at_mslock_token 排序,排第一的记录持有锁。
  • 没拿到锁的命令必须删除自己的锁记录,并返回 metadata_write_conflict
  • expires_at_ms 之后的锁记录会被忽略。
  • 释放锁只删除自己的 <lock_token>.json
  • 旧版本的 .section/agentfs/locks/head.json 如果还存在且未过期,会继续阻塞写入;新版本不再写这个单文件锁。
  • AgentFS 不做无条件覆盖 fallback。backing source 必须支持 metadata 条件创建和锁目录一致列举;不支持时命令失败。

4. Metadata Schemas

All records include schema_version: 1. Readers must reject shared metadata with the wrong schema version, invalid IDs, invalid AgentFS paths, inconsistent grant capabilities, or cross-record links that point at another FS. The JSON error code is malformed_shared_metadata.

fs.json

{
  "schema_version": 1,
  "fs_id": "fs_...",
  "name": "project",
  "owner_agent_id": "agt_...",
  "source_profile_id": "srcp_...",
  "created_at_ms": 1780000000000
}

Grant Record

{
  "schema_version": 1,
  "grant_id": "grt_...",
  "fs_id": "fs_...",
  "agent_id": "agt_...",
  "role": "writer",
  "capabilities": ["read", "commit"],
  "granted_by": "agt_...",
  "created_at_ms": 1780000000000,
  "revoked_at_ms": null,
  "revoked_by": null
}

revoked_at_ms != null makes the grant inactive.

Commit Record

{
  "schema_version": 1,
  "commit_id": "cmt_...",
  "fs_id": "fs_...",
  "parent_commit_id": "cmt_...",
  "base_commit_id": "cmt_...",
  "agent_id": "agt_...",
  "summary": "Update docs",
  "authorized_by": {
    "type": "grant",
    "grant_id": "grt_...",
    "role": "writer",
    "capabilities": ["read", "commit"]
  },
  "paths": [
    {
      "path": "docs/readme.md",
      "kind": "file",
      "op": "update",
      "local_version": "sha256:...",
      "previous_version": "sha256:..."
    }
  ],
  "staging_snapshot": {
    "manifest_path": "agentfs/staging/fs_.../cmt_.../manifest.json",
    "manifest_hash": "sha256:..."
  },
  "created_at_ms": 1780000000000,
  "materialization_state": "pending",
  "materialized_at_ms": null,
  "error": null
}

Allowed op values:

  • create
  • update
  • delete

Allowed materialization_state values:

  • pending
  • materialized
  • failed_to_materialize

authorized_by.type is either owner or grant.

Owner commit:

{
  "type": "owner",
  "agent_id": "agt_..."
}

Grant commit:

{
  "type": "grant",
  "grant_id": "grt_...",
  "role": "writer",
  "capabilities": ["read", "commit"]
}

The commit record keeps this audit even if the grant is revoked later.

heads/current.json

{
  "schema_version": 1,
  "fs_id": "fs_...",
  "commit_id": "cmt_...",
  "updated_at_ms": 1780000000000
}

For a newly created empty FS, commit_id is null.

Event Record

{
  "schema_version": 1,
  "event_id": "evt_1780000000000_0123456789abcdef",
  "seq": 42,
  "fs_id": "fs_...",
  "kind": "commit.accepted",
  "actor_agent_id": "agt_...",
  "subject_id": "cmt_...",
  "path": null,
  "created_at_ms": 1780000000000,
  "data": {}
}

Event path is optional. FS-level events use null.

5. Grant Semantics

MVP roles are FS-wide.

Role Capabilities
owner read, commit, manage
reader read
writer read, commit
manager read, manage

Rules:

  • The creating agent receives an owner grant.
  • Owner cannot be revoked.
  • Ownership transfer is deferred.
  • manager cannot grant owner.
  • manager can create, revoke, or replace reader, writer, and manager grants.
  • manager can create server-side shares for agents that already have readable grants.
  • Revoked agents cannot attach or commit after revocation is observed.
  • Existing raw local files remain on disk after revocation, but they cannot become shared truth through AgentFS commit.

6. Commit Semantics

commit apply commits all dirty paths under the attached root in MVP.

Partial path commits are deferred.

Dirty paths are discovered by comparing the local tree against the last known mounted base state and current path sync state.

Freshness base:

  • the trusted local mount store records base_commit_id for the attached working copy.
  • .section/root.json helps discover the mount identity, but it is not trusted for freshness.
  • heads/current.json records current shared head.
  • commit is rejected with stale_base when the trusted local mount base and current shared head differ.

Rules:

  • summary is required and must be non-empty after trimming whitespace.
  • empty commits are rejected.
  • commits containing .section/** are rejected.
  • reserved metadata paths are ignored in dirty detection.
  • external backing-source drift is treated as conflict or stale state before commit acceptance.

提交输入规则:

  • commit apply 先把本次 dirty paths 复制到本地 staging snapshot。
  • paths[*].local_version 从 staging snapshot 计算。
  • commit record 里的 paths 必须来自 staging manifest。
  • materialization 只能读取 staging snapshot,不能再读 live working tree。
  • 如果 staging snapshot 创建失败,commit 不写 metadata。
  • 如果 live working tree 在 commit 过程中继续变化,这些变化属于下一次 dirty work。

7. Materialization Semantics

Commit acceptance writes governance metadata first:

  1. create staging snapshot,
  2. acquire head lock,
  3. verify grant and freshness,
  4. write commit record with materialization_state: "pending",
  5. update heads/current.json,
  6. write commit.accepted event,
  7. release head lock,
  8. materialize file changes from staging snapshot to backing source,
  9. update commit materialization state,
  10. write commit.materialized, or write both commit.materialization_failed and fs.error,
  11. update local mount store and root marker as a local finalization step.

If step 11 fails after accepted metadata and backing-source materialization have succeeded, the command still succeeds and returns a local warning. The accepted commit remains the governance truth.

If materialization fails:

  • commit remains accepted,
  • FS state becomes error,
  • commit apply is blocked for that FS,
  • attach reports materialization error,
  • repair is required before further accepted commits.

Retry behavior:

  • retry is safe only for commits in pending or failed_to_materialize.
  • retry must use the existing commit_id.
  • retry must not create a second accepted commit.

8. Event Semantics

MVP event kinds:

fs.created
fs.attached
grant.created
grant.revoked
commit.accepted
commit.materialized
commit.materialization_failed
fs.error

Event records are immutable.

Event files are written with backing source create-if-absent / if_not_exists. If .section/agentfs/events/<event_id>.json already exists, the command returns metadata_write_conflict and keeps the old event content unchanged. AgentFS does not fall back to read-then-write overwrite behavior.

Replay:

  • event files are listed under .section/agentfs/events/,
  • clients sort by seq,
  • clients can resume from the last seen seq or event_id.

Ordering:

  • seq is allocated while holding the FS head lock,
  • events emitted under the head lock are ordered relative to commit/grant head mutations.
  • materialization events happen after the accepted commit event.
  • source/path events and AgentFS events may be merged in watch, but each output event must include a stream or kind that identifies its origin.

9. Local Mount Contract

fs attach creates a local working copy.

Attach behavior:

  • checks read capability with Section Control Service,
  • obtains or refreshes a short-lived sync credential,
  • creates or updates the source local-root binding,
  • writes .section/root.json,
  • syncs current materialized backing-source state into the local root,
  • records base_commit_id in the trusted local mount store and mirrors it into .section/root.json.

MVP local root rule:

  • one accepted FS has one active local root per local installation store,
  • attaching the same FS to another local root moves the active root,
  • the old root marker is removed when reattach succeeds.

root.json fields:

{
  "schema_version": 1,
  "fs_id": "fs_...",
  "source_profile_id": "srcp_...",
  "agent_id": "agt_...",
  "installation_id": "ins_...",
  "local_root": "/abs/path/project",
  "base_commit_id": "cmt_...",
  "control_plane_endpoint": "section-control-service"
}

Rules:

  • 一个本地安装 store 里,同一个 FS 只有一个 active local root。
  • 同一个 FS 重新 attach 到另一个 local root,会移动 active root,并移除旧 root marker。
  • reader mounts can be edited locally by the OS, but reader commits are denied.
  • attach should fail or report non-ready state when current head is not materialized.

10. SourceProfile Compatibility

MVP FS binds exactly one server-side SourceProfile.

Rules:

  • fs create asks Section Control Service to bind a SourceProfile.
  • Section Control Service makes the final SourceProfile decision.
  • AgentFS commands that read or write the backing source must refresh a service-issued read credential before constructing the internal source operator.
  • The local cached source config is not authority; it is refreshed from Section Control Service after credential issuance.
  • fs create 必须完整初始化 shared metadata;不能留下半个 FS。
  • 如果 shared metadata 初始化失败,必须回滚 Control Service 的 FS/grant/event 记录。
  • 如果 shared metadata 初始化失败,必须清理本地 AgentFS source cache。
  • 如果 shared metadata 初始化失败,必须尽量清理远端 .section/** 初始化残留。
  • 初始化失败后的同名创建必须可以重试。
  • upgrading an existing source into an FS is deferred.
  • low-level source commands remain sync infrastructure, not the AgentFS product surface.
  • AgentFS-backed sources must be guarded from ordinary source mutation commands.
  • source sync must not become the official way to bypass AgentFS governance.
  • AgentFS commands are the governance surface; source commands are infrastructure.
  • MVP does not provide a low-level force flag to mutate AgentFS-backed sources.

Documentation must not imply that low-level source commands preserve AgentFS governance.

11. Error Model

Errors must have stable codes for JSON output.

Code Meaning Retry
unknown_agent Agent identity is missing or not logged in no, login first
unknown_fs FS metadata cannot be found no, check name/id
grant_denied Agent lacks required capability no, request grant
stale_base local base_commit_id differs from current head yes, sync/refresh first
reserved_metadata_path operation attempts to commit .section/** no
materialization_failed accepted commit could not update backing source yes, repair/retry materialization
malformed_shared_metadata shared metadata failed schema validation no, repair metadata
metadata_write_conflict metadata lock, head update, or immutable event conflict yes

JSON shape:

{
  "error": {
    "code": "grant_denied",
    "message": "agent agt_... does not have commit access to fs fs_...",
    "retryable": false,
    "details": {
      "fs_id": "fs_...",
      "agent_id": "agt_..."
    }
  }
}

Rules:

  • code is stable.
  • message is for humans.
  • retryable is for Agent decisions.
  • details is always present in JSON output. It can be {}.

12. 状态输出合同

section fs status <fs-or-root> --json 是 Agent 判断能不能行动的主要入口。

输出:

{
  "fs": {
    "fs_id": "fs_...",
    "name": "project",
    "head_commit_id": "cmt_...",
    "materialization_state": "materialized"
  },
  "agent": {
    "agent_id": "agt_...",
    "role": "writer",
    "capabilities": ["read", "commit"]
  },
  "mount": {
    "attached": true,
    "mount_id": "hash(fs_id + canonical_local_root)",
    "local_root": "/abs/path/project",
    "base_commit_id": "cmt_..."
  },
  "worktree": {
    "dirty": true,
    "dirty_count": 2,
    "stale": false
  },
  "events": {
    "last_seq": 42
  },
  "warnings": [],
  "next_actions": ["commit"]
}

规则:

  • status 可以解析 FS id、FS name、source name、local root。
  • 非本地路径 ref 的解析顺序是:精确 FS id,精确 source name,精确 FS name。
  • 如果 source name 或 FS name 命中多个候选,返回 ambiguous_fs_ref
  • local root 解析必须看本地 mount store,不能只看 .section/root.json
  • status 不修改共享状态。
  • 如果 Agent 现在不能行动,next_actions 说明下一步安全动作,例如 loginacceptattachsyncrepairrequest_grant

13. 提交快照和修复合同

staging snapshot 路径:

<local-data-dir>/agentfs/staging/<fs_id>/<commit_id>/
  manifest.json
  files/<hash-or-safe-path>

manifest.json 内容:

{
  "schema_version": 1,
  "fs_id": "fs_...",
  "commit_id": "cmt_...",
  "base_commit_id": "cmt_...",
  "created_at_ms": 1780000000000,
  "paths": [
    {
      "path": "docs/readme.md",
      "op": "update",
      "kind": "file",
      "hash": "sha256:...",
      "size": 123,
      "staged_path": "files/sha256-..."
    }
  ]
}

规则:

  • commit apply 写 commit metadata 前先创建 staging snapshot。
  • commit metadata 记录 staging manifest hash。
  • materialization 只能读 staging snapshot。
  • staging 至少保留到 commit 已物化,并且 repair 不再需要它。

section commit repair <fs-or-root> [--commit <commit_id>] 规则:

  • repair 只处理 pendingfailed_to_materialize commit。
  • repair 使用原来的 staging snapshot。
  • repair 不创建新 commit。
  • repair 不移动 head。
  • repair 成功写 commit.materialized
  • repair 失败写 commit.materialization_failedfs.error
  • 如果 staging snapshot 不存在,返回 missing_commit_snapshot

MVP 不提供兜底修复路径。

14. 服务端合同

AgentFS 支持两种 Control Service 接入方式:

  • control_service.endpoint:通过 sectiond serve 提供的 HTTP Control Service。
  • control_service.path:本地 file-backed Control Service,主要用于开发和单机测试。

跨机 / 多 agent 分享必须使用 control_service.endpoint。客户端只配置 endpoint,不配置 SourceProfile 或 backing source 密钥。

服务端负责:

  • agent login
  • installation registration
  • FS create/list/status metadata
  • grant create/revoke/check
  • share create/list/accept/revoke
  • source profile selection
  • short-lived credential issuance
  • AgentFS event replay
  • audit records

当前 MVP 暴露一个 HTTP RPC 入口:

POST /v1/rpc

规则:

  • 跨机 sharing 必须走服务端。
  • HTTP Control Service 请求必须携带当前 agent 的 auth token 和 installation id;服务端不能只信客户端传来的 agent_id
  • SourceProfile 由服务端决定。
  • 远程服务端默认不允许自注册 agent 使用任意 SourceProfile 创建 FS;SourceProfile 必须显式允许创建者。
  • fs create 的 backing source 空目录检查和 AgentFS metadata 初始化由服务端完成。
  • 普通 FS resolve/list/status 不能返回 backing source options。
  • backing source options 只能通过 accept shareissue credential 这种会记录 credential binding 的路径返回给已授权 agent。
  • source 长期密钥不能出现在 share record、local root marker、CLI JSON 输出里。
  • 本地 CLI 可以缓存 identity、accepted FS、mount state、短期 credential binding。
  • 本地 CLI 不能自己发明 grant、share、source profile、长期 credential。
  • 服务端拒绝时,本地 CLI 必须失败关闭。

15. Test Contract

The complete implementation test plan lives in AGENTFS_TEST_PLAN.md.

Minimum behavior tests:

  • owner creates FS and receives owner authority
  • writer can discover, accept, and attach after server-side share
  • reader cannot commit
  • writer can commit a clean local change
  • stale commit is rejected
  • accepted commit emits commit.accepted
  • materialized commit emits commit.materialized
  • second mount observes accepted commit after sync/watch
  • metadata files cannot be committed as normal paths
  • materialization failure produces non-ready state
  • further commits are blocked while head materialization is failed

First Implementation Slice

The first implementation should prove this flow:

agent-a logs in
agent-a creates fs project with a SourceProfile
agent-a grants writer to agent-b
agent-a shares fs project with agent-b
agent-b logs in
agent-b sees fs project in fs available
agent-b accepts the share
agent-b attaches project
agent-b edits a normal file
agent-b commit apply --message "update file"
agent-a watch sees commit.accepted
agent-a sync/attach sees the materialized file

That is the minimum product proof for AgentFS governance.