Źródło: /root/Documents/Obsidian Vault/Hindsight/Nexmoot - Projekt.md
Nexmoot
Nexmoot to control plane dla zewnętrznych agentów AI i automatyzacji: system do bezpiecznego przyjmowania propozycji, rozbijania ich na zadania, przydzielania pracy agentom, zbierania wyników, głosowania, podejmowania decyzji według deterministycznej policy oraz zapewnienia pełnego audytu.
1. Krótka definicja produktu
Nexmoot nie jest runtime'em LLM. Nie uruchamia modeli, nie orkiestruje promptów i nie próbuje być kolejnym frameworkiem agentowym.
Nexmoot jest warstwą kontroli, governance i audytu nad agentami zewnętrznymi.
System odpowiada na pytania:
- kto może zgłosić propozycję,
- kto może przyjąć zadanie,
- kto wykonał pracę,
- kto głosował,
- dlaczego decyzja została podjęta,
- czy da się odtworzyć pełną historię zdarzeń,
- czy system można bezpiecznie zatrzymać,
- czy żaden agent nie zrobił czegoś poza dozwolonym zakresem.
2. One-liner
Nexmoot: audited governance layer for autonomous and semi-autonomous agents.
Po polsku:
Nexmoot: audytowalna warstwa decyzyjna i kontrolna dla zewnętrznych agentów AI.
3. Długoterminowa wizja
Długoterminowo Nexmoot może stać się warstwą koordynacji dla zespołów, które używają wielu agentów AI, botów, automatyzacji CI/CD i ludzi-recenzentów.
Docelowo system może obsługiwać:
- Agent governance
Zarządzanie uprawnieniami agentów, dozwolonymi akcjami, ścieżkami plików, integracjami i limitem autonomii.
- Multi-agent task market
Proposal może generować taski, które różni agenci mogą claimować, wykonywać i submitować.
- Council / review layer
Ludzie, agenci albo hybrydowe rady mogą głosować nad wynikiem pracy.
- Policy engine
Decyzje są podejmowane przez deterministyczne funkcje z pełnymi reasons[], wersjonowaniem policy i audytem.
- Safe external writes
Integracje z GitHubem, Linear, Jira, Notion, Slack/Discord, CI/CD, ale przez bezpieczną warstwę protected paths, kill switch i audyt.
- Agent reputation
System może budować reputację agentów na podstawie jakości submissionów, odsetka zaakceptowanych prac, czasu wykonania i incydentów.
- Compliance / audit dashboard
Pełna historia tego, co zaszło: kto, kiedy, dlaczego, według jakiej policy, z jakim wynikiem.
- Enterprise controls
Tenant isolation, SSO, RBAC/ABAC, signed approvals, export audit logs, legal hold.
4. Zasada strategiczna
Najważniejsza decyzja architektoniczna:
Nexmoot jest control plane, nie runtime LLM.
To oznacza:
- agenci są zewnętrzni,
- Nexmoot nie musi wiedzieć, jak agent myśli,
- Nexmoot kontroluje tylko kontrakt pracy, uprawnienia, stan, decyzje i audyt,
- system nie próbuje rozwiązać wszystkich problemów agent orchestration,
- MVP może być praktyczne i bezpieczne.
5. Główne encje domenowe
Proposal
Proposal to propozycja zmiany, pracy, eksperymentu albo inicjatywy.
Przykłady:
- „Dodaj endpoint X”,
- „Napraw błąd Y”,
- „Zaproponuj refactor modułu Z”,
- „Przygotuj analizę bezpieczeństwa repo”.
Task
Task to konkretna jednostka pracy wynikająca z Proposal.
Task może być claimowany przez agenta albo człowieka.
Submission
Submission to wynik pracy przesłany przez claim ownera.
Może zawierać:
- opis zmian,
- diff,
- link do branch/PR,
- artefakty,
- raport,
- wynik testów,
- uzasadnienie.
Vote
Vote to głos reviewera/council membera nad Submission.
Decision
Decision to finalny wynik procesu: accept, reject, request changes, reopen, no-op.
AuditEvent
AuditEvent to nieusuwalny zapis tego, co zaszło w systemie.
6. MVP cutline
MVP powinno być ograniczone i demonstracyjne, ale kompletne end-to-end.
W MVP robimy
- Level 0 governance,
- fake GitHub adapter,
- protected paths,
- kill switch,
- Proposal / Task / Submission / Vote / Decision,
- state machine,
- idempotencję endpointów krytycznych,
- atomowy claim taska,
- globalny format błędów,
- audit everywhere,
- deterministyczną policy,
- prosty dashboard audytowy,
- golden path demo.
W MVP nie robimy
- prawdziwych autonomicznych merge'y do GitHuba,
- wykonywania kodu agentów wewnątrz Nexmoot,
- skomplikowanego marketplace agentów,
- pełnego ABAC,
- policy marketplace,
- rozbudowanych payoutów/rewardów,
- full enterprise SSO,
- wielopoziomowych organizacji, jeśli nie są potrzebne do demo.
7. Golden path demo
Najważniejszy przepływ demonstracyjny:
1. Admin/proposer tworzy Proposal.
2. Proposal zostaje zaakceptowany.
3. System publikuje Task.
4. Agent claimuje Task.
5. Agent submituje wynik.
6. Reviewer/Council głosuje.
7. Policy podejmuje decyzję.
8. Task zostaje completed albo reopened.
9. Dashboard pokazuje pełną oś czasu audytu.
10. Kill switch może zatrzymać mutujące operacje.
To demo pokazuje wartość produktu bez „magii” i bez udawania, że system samodzielnie rozwiązuje wszystkie problemy agentów.
8. State machine
Status enumy
type ProposalStatus =
| "draft"
| "submitted"
| "under_review"
| "approved"
| "rejected"
| "cancelled"
| "archived";
type TaskStatus =
| "draft"
| "open"
| "claimed"
| "submitted"
| "in_review"
| "completed"
| "cancelled";
type SubmissionStatus =
| "draft"
| "submitted"
| "under_review"
| "changes_requested"
| "accepted"
| "rejected"
| "withdrawn";
expired dla claimów lepiej traktować jako zdarzenie audytowe, niekoniecznie trwały status Taska.
Jedna tabela przejść
| Entity | From | To | Action | Actor | Warunki |
| Proposal | draft | submitted | proposal.submit | proposer | Proposal ma wymagane pola |
| Proposal | submitted | under_review | proposal.start_review | admin, system | Proposal oczekuje na ocenę |
| Proposal | under_review | approved | proposal.approve | admin | Spełnia kryteria |
| Proposal | under_review | rejected | proposal.reject | admin | Wymagany powód |
| Proposal | draft | cancelled | proposal.cancel | proposer, admin | Brak aktywnych Tasków |
| Proposal | submitted | cancelled | proposal.cancel | proposer, admin | Brak aktywnych Tasków |
| Proposal | approved | archived | proposal.archive | admin, system | Wszystkie Taski terminalne |
| Proposal | rejected | draft | proposal.reopen | proposer, admin | Poprawki dozwolone |
| Task | draft | open | task.publish | admin, system | Parent Proposal jest approved |
| Task | open | claimed | task.claim | agent | Brak aktywnego claimu |
| Task | claimed | open | task.release_claim | claim_owner, admin, system | Brak accepted Submission |
| Task | claimed | submitted | task.submit | claim_owner | Claim aktywny i niewygasły |
| Task | submitted | in_review | task.start_review | system, admin | Istnieje Submission |
| Task | in_review | completed | task.complete | system, admin | Policy zaakceptowała Submission |
| Task | in_review | open | task.reopen | system, admin | Policy odrzuca i pozwala retry |
| Task | open | cancelled | task.cancel | admin | Powód wymagany |
| Task | claimed | cancelled | task.cancel | admin | Powód wymagany |
| Task | submitted | cancelled | task.cancel | admin | Powód wymagany |
| Submission | draft | submitted | submission.submit | claim_owner | Task claimnięty przez aktora |
| Submission | submitted | under_review | submission.start_review | system, admin | Payload poprawny |
| Submission | under_review | accepted | submission.accept | system, admin | Policy zwraca accept |
| Submission | under_review | rejected | submission.reject | system, admin | Policy zwraca reject |
| Submission | under_review | changes_requested | submission.request_changes | reviewer, admin, system | Poprawki dozwolone |
| Submission | changes_requested | submitted | submission.resubmit | claim_owner | W terminie |
| Submission | submitted | withdrawn | submission.withdraw | claim_owner | Przed decyzją |
| Submission | under_review | withdrawn | submission.withdraw | claim_owner, admin | Przed decyzją |
| Submission | accepted | — | terminal | — | Brak dalszych przejść |
| Submission | rejected | — | terminal | — | Brak dalszych przejść |
| Submission | withdrawn | — | terminal | — | Brak dalszych przejść |
9. Role i uprawnienia
Minimalne role:
type ActorRole =
| "proposer"
| "agent"
| "reviewer"
| "admin"
| "system";
Rekomendowany model:
can(actor, action, resource): boolean
Przykładowe akcje:
"proposal.submit"
"proposal.approve"
"task.claim"
"task.submit"
"submission.vote"
"task.decide"
"task.cancel"
"system.pause"
"system.resume"
Authz i policy decyzyjna powinny być rozdzielone:
- authz odpowiada: czy aktor może wykonać komendę,
- policy odpowiada: jaka decyzja wynika z danych, głosów i konfiguracji.
10. Idempotencja
Endpointy wymagające Idempotency-Key:
POST /tasks/:taskId/claim
POST /tasks/:taskId/submit
POST /submissions/:submissionId/vote
POST /tasks/:taskId/decide
Tabela:
CREATE TABLE idempotency_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
actor_id UUID NOT NULL,
method TEXT NOT NULL,
endpoint TEXT NOT NULL,
resource_type TEXT NOT NULL,
resource_id UUID NOT NULL,
idempotency_key TEXT NOT NULL,
request_hash TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('processing', 'completed', 'failed')),
response_status INTEGER,
response_body JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL,
UNIQUE (actor_id, method, endpoint, idempotency_key)
);
Zachowanie:
| Sytuacja | Wynik |
| Pierwszy request | Wykonaj i zapisz response |
| Ten sam key + ten sam body | Zwróć zapisany response |
| Ten sam key + inny body | 409 IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_REQUEST |
| Brak key | 400 IDEMPOTENCY_KEY_REQUIRED |
Request w trakcie processing | 409 IDEMPOTENCY_REQUEST_IN_PROGRESS albo Retry-After |
11. Atomowy claim taska
Dla MVP rekomendowany jest atomowy update:
UPDATE tasks
SET
status = 'claimed',
claimed_by = :actor_id,
claimed_at = now(),
claim_expires_at = now() + interval '30 minutes'
WHERE id = :task_id
AND status = 'open'
AND claimed_by IS NULL
RETURNING id, status, claimed_by, claimed_at, claim_expires_at;
Jeśli RETURNING zwraca 0 rekordów, request dostaje:
{
"error": {
"code": "TASK_ALREADY_CLAIMED",
"message": "Task is no longer available for claim.",
"request_id": "req_..."
}
}
Nie wolno robić klasycznego flow SELECT -> if open -> UPDATE bez atomowego warunku.
12. Constraints w DB
CREATE UNIQUE INDEX uniq_accepted_submission_per_task
ON submissions (task_id)
WHERE status = 'accepted';
CREATE UNIQUE INDEX uniq_vote_per_submission_voter
ON votes (submission_id, voter_id);
CREATE UNIQUE INDEX uniq_final_decision_per_task
ON decisions (task_id)
WHERE decision_type IN ('accepted', 'rejected', 'cancelled');
Jeżeli claimy są w osobnej tabeli:
CREATE UNIQUE INDEX uniq_active_claim_per_task
ON task_claims (task_id)
WHERE released_at IS NULL
AND expired_at IS NULL;
13. Globalny format błędów
Każdy błąd ma format:
{
"error": {
"code": "TASK_ALREADY_CLAIMED",
"message": "Task is no longer available for claim.",
"details": {
"task_id": "task-id"
},
"request_id": "req_123"
}
}
Minimalne kody:
type ErrorCode =
| "VALIDATION_ERROR"
| "AUTHENTICATION_REQUIRED"
| "AUTHORIZATION_DENIED"
| "RESOURCE_NOT_FOUND"
| "CONFLICT"
| "INVALID_STATE_TRANSITION"
| "IDEMPOTENCY_KEY_REQUIRED"
| "IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_REQUEST"
| "IDEMPOTENCY_REQUEST_IN_PROGRESS"
| "TASK_ALREADY_CLAIMED"
| "TASK_CLAIM_EXPIRED"
| "TASK_NOT_CLAIMED_BY_ACTOR"
| "SUBMISSION_ALREADY_EXISTS"
| "VOTE_ALREADY_EXISTS"
| "DECISION_ALREADY_EXISTS"
| "POLICY_REJECTED"
| "INTERNAL_ERROR";
Test kontraktowy:
expect(response.body).toEqual({
error: expect.objectContaining({
code: expect.any(String),
message: expect.any(String),
request_id: expect.any(String)
})
});
expect(response.body.error).not.toHaveProperty("stack");
expect(VALID_ERROR_CODES).toContain(response.body.error.code);
14. AuditEvent schema
CREATE TABLE audit_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
occurred_at TIMESTAMPTZ NOT NULL DEFAULT now(),
actor_type TEXT NOT NULL,
actor_id UUID,
action TEXT NOT NULL,
subject_type TEXT NOT NULL,
subject_id UUID NOT NULL,
object_type TEXT,
object_id UUID,
request_id TEXT,
idempotency_key TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb
);
actor_type:
type ActorType = "user" | "agent" | "reviewer" | "admin" | "system";
subject_type:
type SubjectType =
| "proposal"
| "task"
| "submission"
| "vote"
| "decision"
| "policy_evaluation"
| "idempotency_key";
Minimalny AuditAction:
type AuditAction =
| "proposal.created"
| "proposal.submitted"
| "proposal.review_started"
| "proposal.approved"
| "proposal.rejected"
| "proposal.cancelled"
| "proposal.archived"
| "proposal.reopened"
| "task.created"
| "task.published"
| "task.claimed"
| "task.claim_released"
| "task.claim_expired"
| "task.submitted"
| "task.review_started"
| "task.completed"
| "task.reopened"
| "task.cancelled"
| "submission.created"
| "submission.submitted"
| "submission.review_started"
| "submission.accepted"
| "submission.rejected"
| "submission.changes_requested"
| "submission.withdrawn"
| "submission.resubmitted"
| "vote.created"
| "vote.updated"
| "vote.deleted"
| "decision.created"
| "decision.applied"
| "policy.evaluated"
| "policy.accepted"
| "policy.rejected"
| "idempotency.started"
| "idempotency.replayed"
| "idempotency.conflict";
Każda zmiana stanu zapisuje w metadata:
{
"from_status": "open",
"to_status": "claimed",
"reason": "agent_claimed_task"
}
15. Policy jako deterministyczna funkcja
Kontrakt:
function evaluatePolicy(input: PolicyInput): PolicyResult
Policy nie może:
- pisać do DB,
- emitować eventów,
- wysyłać HTTP,
- czytać czasu samodzielnie,
- mutować encji,
- wykonywać losowości.
Input:
type PolicyInput = {
now: string;
policy_name: string;
policy_version: string;
task: {
id: string;
status: TaskStatus;
claimed_by?: string;
claimed_at?: string;
claim_expires_at?: string;
};
submission?: {
id: string;
status: SubmissionStatus;
submitted_by: string;
submitted_at: string;
};
votes: Array<{
voter_id: string;
value: "approve" | "reject" | "abstain";
weight: number;
created_at: string;
}>;
config: {
quorum: number;
min_approvals: number;
min_rejections_to_fail: number;
allow_self_vote: boolean;
voting_deadline?: string;
};
};
Output:
type PolicyResult = {
decision:
| "accept"
| "reject"
| "request_changes"
| "keep_reviewing"
| "reopen_task"
| "no_op";
reasons: Array<{
code: string;
message: string;
facts?: Record<string, unknown>;
}>;
effects: Array<{
type:
| "set_task_status"
| "set_submission_status"
| "create_decision"
| "emit_audit_event";
payload: Record<string, unknown>;
}>;
};
Aplikacja aplikuje effects[] dopiero w transakcji po walidacji allowed transitions.
16. Kill switch
Kill switch musi być pierwszoklasowym guardem przed mutującymi komendami.
Blokuje:
- claim,
- submit,
- vote,
- decide,
- external writes,
- task publish,
- non-admin mutations.
Nie blokuje:
- read-only endpoints,
- audit viewing,
- admin resume/unpause.
Pseudo-flow:
assertSystemNotPaused(commandName);
AuditEvent:
system.paused
system.resumed
command.blocked_by_kill_switch
Można dodać je do enumu, jeśli kill switch jest częścią MVP dashboardu.
17. Protected paths
Fake GitHub / future GitHub adapter powinien od początku modelować protected paths.
Przykład:
type ProtectedPathRule = {
pattern: string;
action: "allow" | "deny" | "require_review";
reason: string;
};
MVP rules:
DENY .env
DENY secrets/**
DENY infra/prod/**
REQUIRE_REVIEW package.json
REQUIRE_REVIEW migrations/**
ALLOW docs/**
ALLOW src/**
18. Architektura aplikacji
Rekomendowany układ logiczny:
API layer
-> request_id
-> authn
-> authz
-> kill switch guard
-> idempotency guard
-> command handler
-> load aggregate
-> validate transition
-> execute domain operation
-> write audit event
-> commit transaction
-> persist idempotent response
-> return response
Dla decide:
POST /tasks/:taskId/decide
-> idempotency
-> transaction
-> load task/submissions/votes FOR UPDATE
-> build PolicyInput
-> evaluatePolicy(input)
-> validate PolicyResult.effects
-> apply effects
-> write Decision
-> write AuditEvents
-> return decision + reasons[]
19. Proponowany stack MVP
Jeśli nie ma jeszcze wybranego stacku, rekomendacja pragmatyczna:
Backend
- TypeScript,
- Node.js,
- Fastify albo NestJS,
- PostgreSQL,
- Prisma albo Drizzle,
- Zod dla walidacji,
- Vitest/Jest dla testów,
- Testcontainers dla testów integracyjnych PostgreSQL.
Frontend dashboard
- Next.js,
- React,
- Tailwind,
- TanStack Query,
- prosty timeline audytu.
Alternatywa bardziej „backend-first”
- Python,
- FastAPI,
- SQLAlchemy,
- Alembic,
- Pydantic,
- pytest,
- PostgreSQL.
Najważniejsze: stack ma wspierać szybkie testy integracyjne transakcji i constraints w PostgreSQL.
20. Plan wdrożenia MVP przez PR-y
PR 1 — Foundation
Cel: uruchomić szkielet systemu.
Zakres:
- repo structure,
- app server,
- PostgreSQL config,
- migrations,
- health endpoint,
- request_id middleware,
- global error handler,
- test runner,
- CI basic.
Acceptance criteria:
GET /healthzwraca OK,- każdy error ma globalny format,
- request_id pojawia się w response i logach,
- testy przechodzą w CI.
PR 2 — Audit core
Cel: audyt jako pierwszoklasowa funkcja.
Zakres:
audit_eventstable,- enumy
AuditAction,SubjectType,ActorType, recordAuditEvent,- endpoint read-only do listowania eventów,
- testy audytu.
Acceptance criteria:
- da się zapisać AuditEvent,
- akcje spoza enumu są odrzucane,
- dashboard/API może pobrać timeline po
subject_id.
PR 3 — Identity i minimal Authz
Cel: minimalny model aktorów i uprawnień.
Zakres:
- actor model,
- role model,
- mock auth dla MVP,
can(actor, action, resource),- permission matrix tests.
Acceptance criteria:
- agent może claimować,
- non-agent nie może claimować,
- reviewer może głosować,
- admin może decydować,
- system actor może wykonywać automatyczne przejścia.
PR 4 — State machine core
Cel: centralny transition validator.
Zakres:
- status enumy,
- transition table,
validateTransition,- testy legal/illegal transitions,
- terminal states.
Acceptance criteria:
- nie da się wykonać przejścia spoza tabeli,
- terminalne statusy blokują mutacje,
- błąd ma
INVALID_STATE_TRANSITION.
PR 5 — Proposal / Task / Submission models
Cel: podstawowy model domenowy.
Zakres:
- tabele
proposals,tasks,submissions, - relacje,
- seed data,
- endpointy read-only,
- podstawowe create/update przez command handlers.
Acceptance criteria:
- Proposal może zostać utworzony,
- approved Proposal może wygenerować Task,
- Task zaczyna jako
open, - każda zmiana stanu tworzy AuditEvent.
PR 6 — Idempotency layer
Cel: ochrona endpointów krytycznych.
Zakres:
idempotency_keystable,- request hashing,
- idempotency guard,
- replay response,
- conflict on changed payload,
- handling
processing.
Acceptance criteria:
- brak
Idempotency-Keyna krytycznym endpoincie zwraca 400, - ten sam key/body zwraca replay,
- ten sam key/inny body zwraca 409,
- replay jest audytowany.
PR 7 — Task claim
Cel: bezpieczny claim.
Zakres:
POST /tasks/:taskId/claim,- atomowy update,
- claim TTL,
- audit event
task.claimed, - concurrency test.
Acceptance criteria:
- dwa równoległe claimy kończą się dokładnie jednym sukcesem,
- drugi request dostaje
TASK_ALREADY_CLAIMED, - task ma
claimed_by,claimed_at,claim_expires_at, - operacja jest idempotentna.
PR 8 — Submit
Cel: claim owner może wysłać wynik.
Zakres:
POST /tasks/:taskId/submit,- walidacja claim ownera,
- constraint na aktywne submissiony,
- audit events,
- idempotency.
Acceptance criteria:
- tylko claim owner może submitować,
- wygasły claim blokuje submit,
- submit zmienia Task na
submitted, - tworzy Submission.
PR 9 — Vote
Cel: reviewer/council może głosować.
Zakres:
POST /submissions/:submissionId/vote,- unique vote per voter/submission,
- opcjonalna aktualizacja vote do deadline,
- self-vote guard,
- audit events.
Acceptance criteria:
- jeden voter nie tworzy duplikatów,
- vote update działa tylko jeśli dozwolone,
- self-vote może być zablokowany przez config,
- głosowanie jest idempotentne.
PR 10 — Policy + Decide
Cel: deterministyczna decyzja.
Zakres:
evaluatePolicy,PolicyInput,PolicyResult,reasons[],effects[],POST /tasks/:taskId/decide,- final decision constraint.
Acceptance criteria:
- policy jest pure function,
- policy ma testy jednostkowe,
- decide aplikuje effects w transakcji,
- nie da się mieć dwóch final decisions dla Taska,
- response zawiera
decisionireasons[].
PR 11 — Kill switch + protected paths
Cel: podstawowe bezpieczeństwo operacyjne.
Zakres:
- system state table/config,
- pause/resume endpoints dla admina,
- kill switch guard,
- fake GitHub protected path checks,
- audit events.
Acceptance criteria:
- paused system blokuje mutujące endpointy,
- read-only nadal działa,
- admin może wznowić system,
- protected paths blokują niedozwolone fake changes.
PR 12 — Dashboard MVP
Cel: czytelny podgląd systemu.
Zakres:
- lista Proposals/Tasks/Submissions,
- audit timeline,
- decision reasons,
- status kill switch,
- podstawowy widok task lifecycle.
Acceptance criteria:
- da się prześledzić golden path,
- każdy status i decyzja są widoczne,
- policy reasons są pokazane,
- audit timeline jest zrozumiały.
PR 13 — Golden path demo
Cel: finalne demo MVP.
Zakres:
- seed scenario,
- fake agent flow,
- fake GitHub submission,
- reviewer vote,
- policy decide,
- dashboard walkthrough,
- E2E test.
Acceptance criteria:
- jeden command/test odpala demo path,
- E2E przechodzi lokalnie i w CI,
- audyt zawiera pełną historię,
- system można zaprezentować bez ręcznego grzebania w DB.
21. Sprinty
Sprint 0 — Decyzje i repo
- wybrać stack,
- założyć repo,
- ustalić naming conventions,
- ustalić strukturę katalogów,
- spisać finalne statusy i akcje.
Sprint 1 — Foundation + Audit
PR 1 + PR 2.
Cel: wszystko, co stanie się później, ma request_id i audyt.
Sprint 2 — Identity + State Machine
PR 3 + PR 4.
Cel: zanim powstaną endpointy mutujące, istnieją centralne reguły uprawnień i przejść.
Sprint 3 — Core Domain + Idempotency
PR 5 + PR 6.
Cel: modele domenowe i bezpieczny retry layer.
Sprint 4 — Claim/Submit/Vote
PR 7 + PR 8 + PR 9.
Cel: agenci mogą wykonać realny lifecycle pracy.
Sprint 5 — Policy/Decide/Safety
PR 10 + PR 11.
Cel: decyzje, kill switch i protected paths.
Sprint 6 — Dashboard + Demo
PR 12 + PR 13.
Cel: gotowe MVP demonstracyjne.
22. Najważniejsze testy
Contract tests
- global error response,
- request_id presence,
- idempotency semantics,
- audit event shape.
State machine tests
- każde dozwolone przejście,
- każde ważne niedozwolone przejście,
- terminal states,
- allowed actors.
Concurrency tests
- równoległy claim,
- równoległy decide,
- retry z tym samym idempotency key,
- retry w stanie processing.
Policy tests
- quorum reached,
- quorum missing,
- approval threshold reached,
- rejection threshold reached,
- self-vote disabled,
- deadline passed,
- deterministic output for same input.
E2E tests
- full golden path,
- rejected submission -> task reopen,
- kill switch blocks mutation,
- protected path blocks fake external write.
23. Rzeczy, na które trzeba uważać
1. Nie rozpraszać logiki domenowej po endpointach
Endpointy powinny być cienkie. Logika ma mieszkać w command handlers, transition validatorze i policy.
2. Nie mieszać authz z policy
Authz: czy aktor może wykonać akcję.
Policy: jaka decyzja wynika z danych.
3. Nie ufać idempotency bez constraints
Idempotency-Key to warstwa API. Integralność musi być też wymuszona przez DB.
4. Nie robić claim przez SELECT potem UPDATE
Claim musi być atomowy.
5. Nie robić policy z side effectami
Policy musi być testowalna i deterministyczna.
6. Nie traktować audytu jako dodatku
Audyt jest częścią produktu, nie logiem developerskim.
7. Nie rozbudowywać MVP przed demo
Największe ryzyko produktowe to scope creep.
8. Nie udawać real GitHuba za wcześnie
Fake GitHub w MVP jest zaletą: pozwala przetestować governance bez ryzyka realnych write'ów.
9. Nie ignorować processing idempotency
Najtrudniejsze bugi pojawią się przy równoległych retry.
10. Nie zostawiać enumów jako luźnych stringów
Statusy, action, subject_type, decision_type i error_code powinny być kontrolowane.
24. Metryki sukcesu MVP
MVP jest udane, jeśli:
- golden path działa end-to-end,
- każdy krok jest audytowany,
- dwa równoległe claimy nie powodują duplikatu,
- retry krytycznych endpointów jest bezpieczny,
- dashboard potrafi wyjaśnić decyzję,
- kill switch działa,
- protected paths działają,
- policy jest testowalna bez DB,
- demo można powtórzyć deterministycznie.
25. Potencjalne rozszerzenia po MVP
V1
- real GitHub integration,
- PR creation,
- branch policies,
- richer dashboard,
- agent identity keys,
- webhook subscriptions,
- scheduled timeout worker,
- policy version UI.
V2
- multi-agent bidding,
- reputation score,
- org/team support,
- ABAC,
- plugin adapters,
- signed decisions,
- exportable audit logs.
V3
- enterprise governance,
- compliance workflows,
- advanced council models,
- reward/payout integration,
- agent marketplace.
26. Krótka ocena projektu
Aktualna ocena:
Wizja produktu: 9/10
Realność MVP: 9/10
Bezpieczeństwo-by-design: 9/10
Gotowość do implementacji: 8.5/10
Ryzyko implementacyjne: umiarkowane, ale kontrolowane
Największy warunek sukcesu:
Nie rozproszyć logiki domenowej po endpointach.
Najlepszy tryb pracy:
mały PR -> test kontraktowy -> audyt -> demo path
27. Decyzje do podjęcia przed pierwszym commitem
- Stack: TypeScript/Fastify/Nest czy Python/FastAPI.
- Czy claim jest kolumnami na
tasks, czy osobną tabelątask_claims. - Czy vote jest immutable, czy updateable do deadline.
- Czy self-vote jest domyślnie zablokowany.
- Czy
proposermoże być reviewerem. - Jak długo trzymamy idempotency keys.
- Czy MVP ma multi-tenant scope, czy single-tenant.
- Czy dashboard jest w tym samym repo, czy osobno.
Rekomendacje domyślne:
Stack: TypeScript + PostgreSQL + Fastify/Nest + Drizzle/Prisma
Claim: kolumny na tasks dla MVP
Vote: updateable do deadline
Self-vote: blocked by default
Proposer as reviewer: blocked by default for MVP
Idempotency TTL: claim 24h, submit/vote 7d, decide 30d
Tenant: single-tenant MVP
Dashboard: same repo for speed
28. Podsumowanie
Nexmoot powinien być budowany jako mały, twardy rdzeń domenowy, a nie szeroka platforma od pierwszego dnia.
MVP ma dowieźć jedną rzecz bardzo dobrze:
Bezpieczny, audytowalny lifecycle pracy zewnętrznego agenta:
Proposal -> Task -> Claim -> Submission -> Vote -> Policy Decision -> Audit Dashboard.
Jeżeli ten przepływ będzie działał deterministycznie, idempotentnie, z pełnym audytem i bez race conditions, projekt będzie miał bardzo mocny fundament pod dalszą rozbudowę.