mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-03 21:27:07 +00:00
Add AgentGit M1: agent-native git layer over a Forgejo backend
AgentGit is a thin, DID-gated source-collaboration layer over a backend forge (default Forgejo at git.profullstack.com, BBS-members-only) — not a new git host. M1 implements the contract and engines: - forge/adapter.ts: ForgeAdapter interface (only forge-specific surface) - forge/forgejo.ts: ForgejoAdapter over Forgejo/Gitea REST v1 (injectable fetch, typed errors), incl. ensureUser for member provisioning - access.ts: gateAccess DID membership gate (owner/role/visibility) - merge-policy.ts: evaluateMergePolicy pure engine (reviews, reputation floor, checks, escrow, merge method, agent-merge toggle) - service.ts: AgentGitService ties gate + policy to the adapter; refuses policy-failing merges; provisionMember hook for AgentBBS - schemas: logicsrc-repo + logicsrc-pull-request, registered in @logicsrc/validators with fixtures - docs/agentgit.md spec; plugin wired into root build (default/disabled) 27 vitest tests pass; full monorepo build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
14fe9d608f
commit
bf046ae280
25 changed files with 1776 additions and 3 deletions
52
plugins/agentgit/src/access.test.ts
Normal file
52
plugins/agentgit/src/access.test.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_MERGE_POLICY, type Repo } from "./domain.js";
|
||||
import { gateAccess } from "./access.js";
|
||||
|
||||
function repo(overrides: Partial<Repo> = {}): Repo {
|
||||
return {
|
||||
type: "logicsrc.repo",
|
||||
version: "1.0",
|
||||
name: "Demo",
|
||||
slug: "demo",
|
||||
owner_did: "owner.example",
|
||||
visibility: "members_only",
|
||||
default_branch: "main",
|
||||
backend: { provider: "forgejo", url: "https://git.profullstack.com" },
|
||||
members: [
|
||||
{ did: "reader.example", role: "reader" },
|
||||
{ did: "contributor.example", role: "contributor" },
|
||||
{ did: "maintainer.example", role: "maintainer" }
|
||||
],
|
||||
merge_policy: DEFAULT_MERGE_POLICY,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe("gateAccess", () => {
|
||||
it("treats the owner as an implicit maintainer", () => {
|
||||
expect(gateAccess(repo(), "owner.example", "merge")).toEqual({ allowed: true, role: "maintainer" });
|
||||
});
|
||||
|
||||
it("denies anonymous access to members_only repos", () => {
|
||||
const result = gateAccess(repo(), undefined, "read");
|
||||
expect(result.allowed).toBe(false);
|
||||
});
|
||||
|
||||
it("allows anonymous read on public repos only", () => {
|
||||
expect(gateAccess(repo({ visibility: "public" }), undefined, "read").allowed).toBe(true);
|
||||
expect(gateAccess(repo({ visibility: "public" }), undefined, "write").allowed).toBe(false);
|
||||
});
|
||||
|
||||
it("denies non-members", () => {
|
||||
const result = gateAccess(repo(), "stranger.example", "read");
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toMatch(/not a member/);
|
||||
});
|
||||
|
||||
it("enforces role capabilities", () => {
|
||||
expect(gateAccess(repo(), "reader.example", "write").allowed).toBe(false);
|
||||
expect(gateAccess(repo(), "contributor.example", "write").allowed).toBe(true);
|
||||
expect(gateAccess(repo(), "contributor.example", "merge").allowed).toBe(false);
|
||||
expect(gateAccess(repo(), "maintainer.example", "merge").allowed).toBe(true);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue