gabriel / musehub public
feat BREAKING task/identity-repo-phase4 #1 / 1
AI Agent gabriel · 142 days ago · May 1, 2026 · Diff

feat: Phase 4 — org creation, member_of relationships, hub routes

- Extend commit_files_to_repo() with delete_paths: reads HEAD manifest, removes the named paths, then merges the result with new files before committing. Used by remove_org_member to tombstone membership files.

- Add musehub/services/musehub_orgs.py: create_org() — creates org MusehubIdentity + identity repo (type='org', pubkey=None, quorum=N) add_org_member() — commits member_of RelationshipRecord to org repo remove_org_member() — commits deletion of membership file from org repo list_org_members() — reads member_of edges from identity repo HEAD

- Add musehub/api/routes/api/orgs.py: POST /api/orgs — create org (201) POST /api/orgs/{org}/members/{handle} — add member (201) GET /api/orgs/{org}/members — list members (200) DELETE /api/orgs/{org}/members/{handle} — remove member (204)

- Register api_orgs_router in main.py before the wildcard musehub router.

- Add tests/test_identity_repo_phase4.py: 20 TDD regression tests covering org creation (handle, type, repo, record, 409, 401), add member (201, relationship committed, record content, 404, 401), list members (empty, populated, 404), remove member (204, absent from list, absent from repo, 404 for nonexistent, 401). All 20 GREEN (47/47 across all four phases).

Closes #19 Phase 4

sha256:e64ee25d42e3cdf9d087cb7c2784d9b07e607d5a043056aa1894ca9daad9a1a1 sha
+80 ~1 symbols
sha256:6a4062ea2c4b5016721488a8c5cea194f772d368590c76f5b45c1c0b2c1c4746 snapshot
+80
symbols added
~1
symbol modified
0
dead code introduced
Semantic Changes 81 symbols
~ musehub/api/routes/api/orgs.py .py 25 symbols added
+ AddMemberRequest class class AddMemberRequest L40–41
+ CreateOrgRequest class class CreateOrgRequest L34–37
+ add_member_route function async_function add_member_route L83–101
+ create_org_route function async_function create_org_route L49–71
+ APIRouter import import APIRouter L15–15
+ AsyncSession import import AsyncSession L17–17
+ AuthError import import AuthError L21–21
+ BaseModel import import BaseModel L16–16
+ Depends import import Depends L15–15
+ Field import import Field L16–16
+ HTTPException import import HTTPException L15–15
+ TokenClaims import import TokenClaims L19–19
+ add_org_member import import add_org_member L22–22
+ annotations import import annotations L11–11
+ create_org import import create_org L22–22
+ get_session import import get_session L20–20
+ list_org_members import import list_org_members L22–22
+ logging import import logging L13–13
+ remove_org_member import import remove_org_member L22–22
+ require_valid_token import import require_valid_token L19–19
+ status import import status L15–15
+ list_members_route function async_function list_members_route L109–119
+ logger variable variable logger L29–29
+ remove_member_route function async_function remove_member_route L131–145
+ router variable variable router L31–31
~ musehub/services/musehub_orgs.py .py 16 symbols added
+ _get_org_identity_repo function async_function _get_org_identity_repo L233–245
+ _head_manifest function async_function _head_manifest L248–265
+ add_org_member function async_function add_org_member L110–151
+ create_org function async_function create_org L17–107
+ AsyncSession import import AsyncSession L9–9
+ AuthError import import AuthError L12–12
+ IntegrityError import import IntegrityError L8–8
+ annotations import import annotations L2–2
+ datetime import import datetime L5–5
+ db import import db L11–11
+ logging import import logging L4–4
+ select import import select L7–7
+ timezone import import timezone L5–5
+ list_org_members function async_function list_org_members L192–227
+ logger variable variable logger L14–14
+ remove_org_member function async_function remove_org_member L154–189
~ tests/test_identity_repo_phase4.py .py 38 symbols added
+ TestAddOrgMember class class TestAddOrgMember L188–245
+ test_add_member_commits_relationship_to_org_identity_repo method async_method test_add_member_commits_relationship_to_org_identity_repo L200–210
+ test_add_member_relationship_record_content method async_method test_add_member_relationship_record_content L212–226
+ test_add_member_requires_auth method async_method test_add_member_requires_auth L238–245
+ test_add_member_returns_201 method async_method test_add_member_returns_201 L189–198
+ test_add_member_unknown_org_returns_404 method async_method test_add_member_unknown_org_returns_404 L228–236
+ TestListOrgMembers class class TestListOrgMembers L253–278
+ test_list_members_empty_after_creation method async_method test_list_members_empty_after_creation L254–260
+ test_list_members_includes_added_member method async_method test_list_members_includes_added_member L262–272
+ test_list_members_unknown_org_returns_404 method async_method test_list_members_unknown_org_returns_404 L274–278
+ TestOrgCreation class class TestOrgCreation L100–180
+ test_create_org_creates_identity_repo method async_method test_create_org_creates_identity_repo L133–146
+ test_create_org_duplicate_handle_returns_409 method async_method test_create_org_duplicate_handle_returns_409 L162–171
+ test_create_org_identity_record_type_is_org method async_method test_create_org_identity_record_type_is_org L148–160
+ test_create_org_requires_auth method async_method test_create_org_requires_auth L173–180
+ test_create_org_response_has_correct_handle method async_method test_create_org_response_has_correct_handle L111–120
+ test_create_org_response_type_is_org method async_method test_create_org_response_type_is_org L122–131
+ test_create_org_returns_201 method async_method test_create_org_returns_201 L101–109
+ TestRemoveOrgMember class class TestRemoveOrgMember L286–343
+ test_remove_member_absent_from_members_list method async_method test_remove_member_absent_from_members_list L299–312
+ test_remove_member_relationship_file_absent_from_repo method async_method test_remove_member_relationship_file_absent_from_repo L314–327
+ test_remove_member_requires_auth method async_method test_remove_member_requires_auth L339–343
+ test_remove_member_returns_204 method async_method test_remove_member_returns_204 L287–297
+ test_remove_nonexistent_member_returns_404 method async_method test_remove_nonexistent_member_returns_404 L329–337
+ _add_member function async_function _add_member L49–62
+ _create_org function async_function _create_org L33–46
+ _get_identity_repo_head_manifest function async_function _get_identity_repo_head_manifest L65–85
+ _read_object function async_function _read_object L88–92
+ AsyncClient import import AsyncClient L21–21
+ AsyncSession import import AsyncSession L24–24
+ Path import import Path L22–22
+ StrDict import import StrDict L27–27
+ annotations import import annotations L15–15
+ db import import db L26–26
+ json import import json L17–17
+ msgpack import import msgpack L20–20
+ pytest import import pytest L19–19
+ select import import select L23–23
~ musehub/main.py .py 1 symbol added
+ api_orgs_router import import api_orgs_router L63–63
← Older Oldest on task/identity-repo-phase4
All commits
Newer → Latest on task/identity-repo-phase4

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:e64ee25d42e3cdf9d087cb7c2784d9b07e607d5a043056aa1894ca9daad9a1a1 --body "your comment"