centergit

package
v0.0.0-...-c9b462d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package centergit implements center-hosted git storage for agent and team memory — the "方案 A" of the Team 一等实体 design (docs/design/features/2026-07-12-team-entity-design.md §4.2/§4.3/§9).

The center hosts one bare git repo per agent, per team, plus a single global repo. Runtimes clone/pull/push over git's smart-HTTP protocol, reusing the center's existing per-agent bearer token for auth. This package provides:

  • Host (host.go): bare-repo provisioning on the center's disk (git init --bare), per-agent / per-team / global (§4.2, §4.3 write path).
  • Authorizer + TeamMembership (authz.go): the access-control decision — "does this token's agent belong to the repo's team → rw; global repo is readable by all; an agent may rw its own repo" (§9 访问控制映射). TeamMembership is the seam onto S1's team service (center maintains the agent→team mapping); a concurrency-safe in-memory MapMembership ships here for bootstrap and tests, and models "实例化时给新 agent 授权其 team repo" via Grant.
  • Handler (httpbackend.go): a git smart-HTTP endpoint that authenticates the caller, authorizes read (upload-pack) vs write (receive-pack) against the requested repo, then bridges to git-http-backend via net/http/cgi.
  • Store (store.go): the client-side memory store that keeps 每条经验一文件 (slug/uuid-named entry files) with a single MEMORY.md index DERIVED from the entries (never hand-edited), and pushes with pull-rebase-retry to absorb concurrent team writes (§5 渐进式加载 index, §9 并发写).

Integration note (S2 stacks on S1): wiring Handler into the admin API's router and backing TeamMembership with S1's team sqlite tables is a single step performed once S1's Team entity lands; see Handler docs for the exact AgentResolver contract against the admin auth middleware.

Index

Constants

View Source
const (
	RuleDescriptionMaxBytes = 240
	RuleIndexMaxEntries     = 64
	RuleIndexMaxBytes       = 16 * 1024
)
View Source
const (
	ProposalStatusPending  = "pending"
	ProposalStatusPromoted = "promoted"
	ProposalStatusRejected = "rejected"

	MemoryItemEntry    = "entry"
	MemoryItemRule     = "rule"
	MemoryItemProposal = "proposal"
	MemoryItemIndex    = "index"

	TeamMemoryEffectHint = "" /* 160-byte string literal not displayed */
)
View Source
const RuleRefreshSemantics = "" /* 200-byte string literal not displayed */

Variables

View Source
var (
	// ErrUnauthenticated means no agent identity was resolved from the request.
	ErrUnauthenticated = errors.New("centergit: unauthenticated")
	// ErrForbidden means the agent is known but not permitted for this repo/op.
	ErrForbidden = errors.New("centergit: forbidden")
)

Access-control sentinels. Callers map these to HTTP 401 / 403.

View Source
var (
	ErrRuleSnapshotNotFound  = errors.New("rule_snapshot_not_found")
	ErrTeamRuleNotFound      = errors.New("team_rule_not_found")
	ErrTeamRuleIndexTooLarge = errors.New("team_rule_index_too_large")
)
View Source
var (
	// ErrPushRetriesExhausted means pull-rebase-retry ran out of attempts while
	// racing concurrent writers.
	ErrPushRetriesExhausted = errors.New("centergit: push retries exhausted")
	// ErrInvalidEntry means an Entry failed validation.
	ErrInvalidEntry = errors.New("centergit: invalid entry")
	// ErrInvalidRule means a Rule failed validation.
	ErrInvalidRule = errors.New("centergit: invalid rule")
)

Store errors.

View Source
var (
	ErrTeamMemoryNotConfigured      = errors.New("team memory: service not configured")
	ErrTeamMemoryNotFound           = errors.New("team memory: not found")
	ErrTeamMemoryInvalidProposal    = errors.New("team memory: invalid proposal")
	ErrTeamMemoryProposalNotPending = errors.New("team memory: proposal is not pending")
	ErrTeamMemoryWarningAckRequired = errors.New("team memory: warning acknowledgement required")
	ErrTeamMemoryAgentSelfGrant     = errors.New("team memory: agents cannot self-grant curator access")
	ErrTeamMemoryInvalidSettings    = errors.New("team memory: invalid settings")
)
View Source
var ErrGitOpFailed = errors.New("centergit: git operation failed")

ErrGitOpFailed wraps a failed git plumbing invocation.

View Source
var ErrHostRootEmpty = errors.New("centergit: host root is empty")

ErrHostRootEmpty is returned when a Host is constructed without a root dir.

View Source
var ErrInvalidRepoRef = errors.New("centergit: invalid repo ref")

ErrInvalidRepoRef is returned for a malformed or unsafe RepoRef.

Functions

func RuleAppliesToPhase

func RuleAppliesToPhase(r Rule, phase string) bool

RuleAppliesToPhase reports whether r is enabled and active in phase.

Types

type AgentResolver

type AgentResolver func(*http.Request) (agentID string, ok bool)

AgentResolver extracts the authenticated agent id from a request. In the admin API this is backed by the bearer-token auth middleware: resolve the worker token → the operating agent (requireAgentOnWorker) → agent.ID(). It returns ok=false when no agent identity is present (→ HTTP 401).

type Author

type Author struct {
	Name  string
	Email string
}

Author is the git identity a Store commits under.

type Authorizer

type Authorizer struct {
	// contains filtered or unexported fields
}

Authorizer decides whether an authenticated agent may read/write a repo, implementing the §4.2/§9 rules:

  • global repo : read = every authenticated agent; write = forbidden (platform-level, not agent-writable).
  • agent repo : rw iff the requesting agent owns the repo.
  • team repo : rw iff the agent's team == the repo's team.

func NewAuthorizer

func NewAuthorizer(m TeamMembership) *Authorizer

NewAuthorizer wires an Authorizer over a TeamMembership source.

func (*Authorizer) Authorize

func (a *Authorizer) Authorize(ctx context.Context, agentID string, ref RepoRef, op Operation) error

Authorize returns nil when agentID may perform op on ref, else ErrUnauthenticated / ErrForbidden (or a backing-store error).

type CreateMemoryProposalInput

type CreateMemoryProposalInput struct {
	TargetKind          string
	Slug                string
	Title               string
	Description         string
	Body                string
	Enabled             bool
	AppliesTo           []string
	WarningAcknowledged bool
	AuthorRef           string
	Author              Author
}

type Entry

type Entry struct {
	// Slug is the human, path-safe stem of the file name (e.g.
	// "prefer-table-driven-tests"). Combined with a uuid it forms the file name.
	Slug string
	// Title is an optional heading for the entry body.
	Title string
	// Description is the one-line hook that lands in the index.
	Description string
	// Body is the markdown content (without frontmatter).
	Body string
	// Type is an optional classification (user/feedback/project/reference…).
	Type string
	// SourcePath is populated by readers with the repo-relative path.
	SourcePath string
	// UUID is populated by readers from the entry frontmatter.
	UUID string
}

Entry is one memory experience. Design §9 mandates 每条经验一文件 so concurrent writers touch different files (git auto-merges), and the shared MEMORY.md index is DERIVED from entries — never hand-edited.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is the center-hosted git smart-HTTP endpoint (§4.2/§4.3). Per request it: (1) resolves the caller's agent id, (2) parses the target RepoRef and whether the operation reads (upload-pack) or writes (receive-pack), (3) authorizes via Authorizer, then (4) bridges to git-http-backend over CGI.

Mount it behind the admin auth+deps middleware, e.g. at "/admin/git/"; set MountPrefix to that value so the handler can recover the bare repo path.

func NewHandler

func NewHandler(host *Host, authz *Authorizer, resolve AgentResolver, opts ...HandlerOption) (*Handler, error)

NewHandler wires a Handler. When the git-http-backend path is not overridden it is discovered via `git --exec-path`.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type HandlerOption

type HandlerOption func(*Handler)

HandlerOption configures a Handler.

func WithExtraEnv

func WithExtraEnv(env ...string) HandlerOption

WithExtraEnv appends env vars to every CGI invocation.

func WithHTTPBackend

func WithHTTPBackend(path string) HandlerOption

WithHTTPBackend overrides the git-http-backend binary path (tests / non-default git installs).

func WithMountPrefix

func WithMountPrefix(p string) HandlerOption

WithMountPrefix sets the URL prefix the handler is mounted at; it is trimmed from the request path before the repo path is parsed.

type Host

type Host struct {
	// contains filtered or unexported fields
}

Host owns the center's bare-repo tree: one bare repo per agent / team plus a single global repo, all under root (§4.2 "center 每个 agent/team 建一个 bare repo 在自己盘上"). It is the provisioning surface — runtimes never touch these dirs directly, they clone/push over the smart-HTTP Handler.

func NewHost

func NewHost(root string, runner memory.GitRunner) *Host

NewHost wires a Host at root. A nil runner defaults to the real git binary.

func (*Host) EnsureRepo

func (h *Host) EnsureRepo(ctx context.Context, ref RepoRef) error

EnsureRepo idempotently provisions ref's bare repo: git init --bare (initial branch main) plus http.receivepack=true so authenticated push works over smart-HTTP. Provisioning per-agent / per-team repos and, at instantiation, a team's shared repo, all funnel through here (§4.2/§4.3, §9 provisioning).

func (*Host) RepoDir

func (h *Host) RepoDir(ref RepoRef) (string, error)

RepoDir returns the absolute on-disk bare-repo directory for ref.

func (*Host) RepoExists

func (h *Host) RepoExists(ref RepoRef) (bool, error)

RepoExists reports whether ref's bare repo has been provisioned (probed via the presence of its HEAD file, which git init --bare always writes).

func (*Host) Root

func (h *Host) Root() string

Root is the absolute directory holding all bare repos (== git-http-backend's GIT_PROJECT_ROOT).

type MapMembership

type MapMembership struct {
	// contains filtered or unexported fields
}

MapMembership is a concurrency-safe in-memory TeamMembership. It also models "实例化时给新 agent 授权其 team repo" (§9): instantiation calls Grant to record the new agent's team, which immediately unlocks rw on that team's repo.

func NewMapMembership

func NewMapMembership() *MapMembership

NewMapMembership returns an empty membership map.

func (*MapMembership) Grant

func (m *MapMembership) Grant(agentID, teamID string)

Grant records that agentID belongs to teamID (agent 独占一个 team — this overwrites any prior team for the agent).

func (*MapMembership) Revoke

func (m *MapMembership) Revoke(agentID string)

Revoke removes the agent's team membership.

func (*MapMembership) TeamOfAgent

func (m *MapMembership) TeamOfAgent(_ context.Context, agentID string) (string, bool, error)

TeamOfAgent implements TeamMembership.

type MemoryDocument

type MemoryDocument struct {
	Kind        string
	Slug        string
	Path        string
	Title       string
	Frontmatter string
	Body        string
	UUID        string
	Commit      string
	Proposal    *MemoryProposal
}

type MemoryItem

type MemoryItem struct {
	Kind        string
	Slug        string
	Path        string
	Title       string
	Description string
	Scope       string
	UUID        string
	Commit      string
	Enabled     bool
	AppliesTo   []string
}

type MemoryProposal

type MemoryProposal struct {
	ID                  string
	UUID                string
	Status              string
	TargetKind          string
	Slug                string
	Title               string
	Description         string
	Body                string
	AuthorRef           string
	CreatedAt           string
	UpdatedAt           string
	SourcePath          string
	PromotedPath        string
	TargetUUID          string
	Commit              string
	Enabled             bool
	AppliesTo           []string
	WarningAcknowledged bool
	RejectReason        string
	Diff                string
}

type Operation

type Operation int

Operation is a smart-HTTP access mode: OpRead (git-upload-pack / clone / pull) or OpWrite (git-receive-pack / push).

const (
	// OpRead corresponds to git-upload-pack (fetch/clone/pull).
	OpRead Operation = iota
	// OpWrite corresponds to git-receive-pack (push).
	OpWrite
)

func (Operation) String

func (o Operation) String() string

String renders the operation for logs/errors.

type ProducerOption

type ProducerOption func(*TeamMemoryProducer)

ProducerOption configures a TeamMemoryProducer.

func WithSeedAuthor

func WithSeedAuthor(a Author) ProducerOption

WithSeedAuthor overrides the git author the seed commit is attributed to.

type PromoteMemoryProposalInput

type PromoteMemoryProposalInput struct {
	ProposalID          string
	WarningAcknowledged bool
	ActorRef            string
	Author              Author
}

type RejectMemoryProposalInput

type RejectMemoryProposalInput struct {
	ProposalID string
	Reason     string
	ActorRef   string
	Author     Author
}

type RepoKind

type RepoKind string

RepoKind enumerates the three center-hosted git repo scopes (§4.2).

const (
	// RepoKindAgent is a single agent's private memory repo.
	RepoKindAgent RepoKind = "agent"
	// RepoKindTeam is a team's shared memory repo (all members rw).
	RepoKindTeam RepoKind = "team"
	// RepoKindGlobal is the single platform-level repo (all agents read).
	RepoKindGlobal RepoKind = "global"
)

type RepoRef

type RepoRef struct {
	Kind RepoKind
	ID   string
}

RepoRef identifies one bare repo on the center host. For RepoKindGlobal the ID is empty (there is exactly one global repo).

func AgentRepo

func AgentRepo(id string) RepoRef

AgentRepo returns the ref for agent id's private repo.

func GlobalRepo

func GlobalRepo() RepoRef

GlobalRepo returns the ref for the single global repo.

func TeamRepo

func TeamRepo(id string) RepoRef

TeamRepo returns the ref for team id's shared repo.

func (RepoRef) String

func (r RepoRef) String() string

String is a stable, human-readable identifier for logs/errors.

func (RepoRef) Validate

func (r RepoRef) Validate() error

Validate checks the ref is well-formed and its ID is a safe single path segment (no separators, no traversal, no leading dot). Global refs must carry no ID.

type Rule

type Rule struct {
	// Slug is the human, path-safe stem of the file name.
	Slug string
	// Title is an optional heading for the rule body.
	Title string
	// Description is the one-line hook that lands in the index.
	Description string
	// Body is the markdown content (without frontmatter).
	Body string
	// Enabled gates whether the rule is loaded into runtime context.
	Enabled bool
	// AppliesTo names the phases where the rule is active. Empty normalizes to
	// all phases when writing; accepted values are plan, execute, review,
	// recovery, or all.
	AppliesTo []string
	// SourcePath is populated by readers with the repo-relative path.
	SourcePath string
	// UUID is populated by readers from the rule frontmatter.
	UUID string
}

Rule is one team-scoped operational rule. Rules live under rules/; that directory, not a frontmatter kind, is the only source of their type.

type RuleBodySnapshot

type RuleBodySnapshot struct {
	TeamID           string
	Phase            string
	Commit           string
	Rule             Rule
	RefreshSemantics string
}

RuleBodySnapshot records one commit-bound rule body read.

type RuleIndexEntry

type RuleIndexEntry struct {
	Slug        string   `json:"slug"`
	Title       string   `json:"title,omitempty"`
	Description string   `json:"description"`
	AppliesTo   []string `json:"applies_to"`
	BodyBytes   int      `json:"body_bytes"`
	SourcePath  string   `json:"source_path,omitempty"`
}

RuleIndexEntry is the body-free startup/runtime projection for one rule.

type RuleIndexSnapshot

type RuleIndexSnapshot struct {
	TeamID           string
	Phase            string
	Commit           string
	Rules            []RuleIndexEntry
	Skipped          []string
	RefreshSemantics string
}

RuleIndexSnapshot records the exact commit used to build a phase-scoped index.

type RuleSnapshot

type RuleSnapshot struct {
	TeamID           string
	Phase            string
	Commit           string
	Rules            []Rule
	Skipped          []string
	RefreshSemantics string
}

RuleSnapshot is the auditable result of loading team rules from a repo commit.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the client-side (runtime) view of a checked-out center repo working copy. It writes per-entry files, regenerates the index deterministically, and pushes with pull-rebase-retry to absorb concurrent team writes (§5, §9).

func NewStore

func NewStore(dir string, runner memory.GitRunner, opts ...StoreOption) *Store

NewStore wires a Store over the working-copy dir. A nil runner defaults to the real git binary; the default uuid source is a ULID.

func (*Store) Commit

func (s *Store) Commit(ctx context.Context, author Author, message string) error

Commit stages the whole working tree and commits under author. It is a no-op (returns nil) when the tree is clean.

func (*Store) ListEntries

func (s *Store) ListEntries() ([]entryIndexRow, error)

ListEntries parses every entries/*.md file's frontmatter. Entries are sorted by (name, file) for a stable, deterministic order.

func (*Store) ListRules

func (s *Store) ListRules() ([]ruleIndexRow, error)

ListRules parses every rules/*.md file's frontmatter. Rules are sorted by (name, file) for a stable, deterministic order.

func (*Store) ReadEntries

func (s *Store) ReadEntries() (entries []Entry, skipped []string, err error)

ReadEntries parses every entries/*.md file into a FULL Entry (frontmatter + body + type). Unlike ListEntries — which yields only the index projection (name/description/file) — this reconstructs each experience so a caller such as extract_from_team can carry it into a draft template. Sorted by (slug, file) for a deterministic order.

DEFENSIVE (design §6 extract): a team member may push ANY non-standard / stray file into the shared team repo (a note without frontmatter, a scratch file, …). A single malformed file must NOT crash the whole extract. Such files are SKIPPED and their names returned in `skipped` so the caller can flag "skipped N non-standard entries" in the draft/response for the curator. Only a genuine IO error (not a content-format problem) surfaces as an error. This read path is deliberately lenient; the WRITE-side index derivation (ListEntries / RegenerateIndex) stays strict — a writer controls its own files.

func (*Store) ReadRules

func (s *Store) ReadRules() (rules []Rule, skipped []string, err error)

ReadRules parses every rules/*.md file into a FULL Rule. Malformed rule files are skipped and returned to the caller; genuine IO errors surface.

func (*Store) RegenerateIndex

func (s *Store) RegenerateIndex() error

RegenerateIndex rebuilds MEMORY.md purely from entries/ and rules/ (§9: 索引从条目派生、不手编). The output is deterministic so identical memory sets on two runtimes produce byte-identical indexes → no spurious merge conflicts.

func (*Store) SyncPush

func (s *Store) SyncPush(ctx context.Context, remote, branch string, author Author, message string, maxRetries int) error

SyncPush regenerates the index, commits, then pushes to remote/branch. On a non-fast-forward rejection (a concurrent team writer landed first) it runs pull --rebase and retries, up to maxRetries times — the §9 "push 前 pull-rebase-retry 兜并发写" contract. Because entries are per-file, the rebase almost never conflicts; only the derived index could, and it is regenerated deterministically after each rebase.

func (*Store) ValidateRuleIndexBudgets

func (s *Store) ValidateRuleIndexBudgets() error

ValidateRuleIndexBudgets enforces the phase-scoped rule-index budget that new runtime contexts depend on. It reads complete rule bodies so body_bytes is accurate, but does not persist or mutate anything.

func (*Store) WriteEntry

func (s *Store) WriteEntry(e Entry) (string, error)

WriteEntry persists e as entries/<slug>-<uuid>.md and returns the repo-relative path. It does NOT commit or regenerate the index — callers batch those (see RegenerateIndex + SyncPush) so a burst of writes is one commit.

func (*Store) WriteRule

func (s *Store) WriteRule(r Rule) (string, error)

WriteRule persists r as rules/<slug>-<uuid>.md and returns the repo-relative path. Like WriteEntry, it does not commit; callers batch via RegenerateIndex and SyncPush.

type StoreOption

type StoreOption func(*Store)

StoreOption configures a Store.

func WithHomeOverride

func WithHomeOverride(home string) StoreOption

WithHomeOverride sets HOME/XDG_CONFIG_HOME for git invocations (test hygiene).

func WithIDGen

func WithIDGen(fn func() string) StoreOption

WithIDGen injects the entry uuid generator (tests use a deterministic one).

type TeamMembership

type TeamMembership interface {
	// TeamOfAgent returns the team the agent belongs to. ok=false means the
	// agent is in no team (yet). err is reserved for backing-store failures.
	TeamOfAgent(ctx context.Context, agentID string) (teamID string, ok bool, err error)
}

TeamMembership is the seam onto S1's team service: the center maintains the agent→team mapping and this port answers "which team does this agent belong to" (§9 访问控制映射). An agent 独占一个 team, so at most one team id.

S1's Team entity backs this with sqlite; S2 ships MapMembership for bootstrap and tests.

type TeamMemoryConsumer

type TeamMemoryConsumer struct {
	// contains filtered or unexported fields
}

TeamMemoryConsumer reads a team's center-hosted memory repo.

func NewTeamMemoryConsumer

func NewTeamMemoryConsumer(host *Host, runner memory.GitRunner) *TeamMemoryConsumer

NewTeamMemoryConsumer wires a consumer over host. A nil runner defaults to the real git binary (memory.NewExecGitRunner).

func (*TeamMemoryConsumer) ReadTeam

func (c *TeamMemoryConsumer) ReadTeam(ctx context.Context, teamID string) (entries []Entry, skipped []string, err error)

ReadTeam clones team teamID's bare repo into a throwaway working copy and returns every memory entry (frontmatter + body). A team whose repo has not been provisioned yet (no memory seeded) yields nil, nil, nil — an absent history is not an error, it is simply an empty experience set.

The returned `skipped` list names any non-standard files in the repo that are NOT well-formed memory entries (no frontmatter, etc.): they are skipped rather than crashing the read, so a member's stray push cannot break extract_from_team (design §6). Callers surface the count for the curator.

func (*TeamMemoryConsumer) ReadTeamAllRules

func (c *TeamMemoryConsumer) ReadTeamAllRules(ctx context.Context, teamID string) (rules []Rule, skipped []string, err error)

ReadTeamAllRules returns every rule file regardless of enabled/applies_to. It is used by extract/template flows; runtime context should use ReadTeamRules so disabled or non-matching rules do not leak into a run.

func (*TeamMemoryConsumer) ReadTeamRule

func (c *TeamMemoryConsumer) ReadTeamRule(ctx context.Context, teamID, phase, slug, commit string) (RuleBodySnapshot, error)

ReadTeamRule reads one enabled, phase-applicable rule body from the exact commit supplied by a prior index response. It never falls back to HEAD.

func (*TeamMemoryConsumer) ReadTeamRuleIndex

func (c *TeamMemoryConsumer) ReadTeamRuleIndex(ctx context.Context, teamID, phase string) (RuleIndexSnapshot, error)

ReadTeamRuleIndex returns a body-free, phase-scoped rule index from the team's current HEAD commit. A missing team repo yields an empty snapshot. Malformed rule files are skipped and reported.

func (*TeamMemoryConsumer) ReadTeamRules

func (c *TeamMemoryConsumer) ReadTeamRules(ctx context.Context, teamID, phase string) (RuleSnapshot, error)

ReadTeamRules clones team teamID's bare repo into a throwaway working copy and returns the enabled rules that apply to phase plus the exact HEAD commit used. A missing team repo yields an empty snapshot, not an error. Malformed rule files are skipped and reported, mirroring ReadTeam's defensive contract.

type TeamMemoryProducer

type TeamMemoryProducer struct {
	// contains filtered or unexported fields
}

TeamMemoryProducer seeds a team's center-hosted memory repo from a set of portable experiences. It owns no state beyond the Host it provisions against and the git runner it drives.

func NewTeamMemoryProducer

func NewTeamMemoryProducer(host *Host, runner memory.GitRunner, opts ...ProducerOption) *TeamMemoryProducer

NewTeamMemoryProducer wires a producer over host. A nil runner defaults to the real git binary (memory.NewExecGitRunner).

func (*TeamMemoryProducer) SeedTeam

func (p *TeamMemoryProducer) SeedTeam(ctx context.Context, teamID string, entries []Entry, ruleSets ...[]Rule) (int, error)

SeedTeam provisions (idempotently) team teamID's bare repo and writes each entry/rule into it as one file, pushing a single seed commit. Entries/rules that fail per-item validation are skipped — seeding is best-effort over a human-curated template. Returns the number of items actually written. A nil/zero item set is a no-op (repo still provisioned). The variadic rules parameter preserves the original entries-only call shape.

type TeamMemoryRepository

type TeamMemoryRepository struct {
	// contains filtered or unexported fields
}

TeamMemoryRepository is the Git-backed Repository adapter for ADR-0057. Each command uses a fresh clone of the team's bare repo, and the pushed main HEAD is the aggregate version.

func NewTeamMemoryRepository

func NewTeamMemoryRepository(host *Host, runner memory.GitRunner, opts ...TeamMemoryRepositoryOption) *TeamMemoryRepository

NewTeamMemoryRepository wires the Git-backed Team Memory repository.

func (*TeamMemoryRepository) Bootstrap

Bootstrap applies trusted seed content to canonical memory. It exists so Producer/migration do not keep a separate long-term Store bypass.

func (*TeamMemoryRepository) BootstrapWithPaths

func (r *TeamMemoryRepository) BootstrapWithPaths(ctx context.Context, teamID string, cmd TrustedBootstrapCommand) (int, []string, string, error)

BootstrapWithPaths is the detailed form used by migrations that must report exact rollback paths.

func (*TeamMemoryRepository) Get

func (r *TeamMemoryRepository) Get(ctx context.Context, teamID, proposalID string) (teammemory.ProposalView, error)

Get reads one proposal by id from one team repo.

func (*TeamMemoryRepository) List

List returns proposals in the team repo. Default status is pending.

func (*TeamMemoryRepository) Propose

Propose writes a pending proposal under proposals/ and pushes it. Concurrent unrelated proposals retry from a fresh clone so both commits survive without last-write-wins.

func (*TeamMemoryRepository) Review

Review promotes or rejects one pending proposal. Promotion updates canonical memory, proposal status, and MEMORY.md in one commit; push races return a fail-loud version conflict.

type TeamMemoryRepositoryOption

type TeamMemoryRepositoryOption func(*TeamMemoryRepository)

TeamMemoryRepositoryOption configures the Git repository adapter.

func WithProposalIDGen

func WithProposalIDGen(fn func() string) TeamMemoryRepositoryOption

WithProposalIDGen injects deterministic proposal ids for tests. The function may return either a bare id or a tmprop- prefixed id.

func WithRepositoryAuthor

func WithRepositoryAuthor(a Author) TeamMemoryRepositoryOption

WithRepositoryAuthor overrides the stable system git identity.

func WithRepositoryClock

func WithRepositoryClock(fn func() time.Time) TeamMemoryRepositoryOption

WithRepositoryClock injects deterministic timestamps.

type TeamMemoryService

type TeamMemoryService struct {
	// contains filtered or unexported fields
}

func NewTeamMemoryService

func NewTeamMemoryService(host *Host, runner memory.GitRunner, opts ...TeamMemoryServiceOption) *TeamMemoryService

func (*TeamMemoryService) Configured

func (s *TeamMemoryService) Configured() bool

func (*TeamMemoryService) CreateProposal

func (*TeamMemoryService) GetDocument

func (s *TeamMemoryService) GetDocument(ctx context.Context, teamID, kind, slug string) (MemoryDocument, error)

func (*TeamMemoryService) GetSettings

func (s *TeamMemoryService) GetSettings(ctx context.Context, teamID string) (TeamMemorySettings, error)

func (*TeamMemoryService) List

func (*TeamMemoryService) PromoteProposal

func (*TeamMemoryService) RejectProposal

func (*TeamMemoryService) UpdateSettings

type TeamMemoryServiceOption

type TeamMemoryServiceOption func(*TeamMemoryService)

func WithTeamMemoryClock

func WithTeamMemoryClock(fn func() time.Time) TeamMemoryServiceOption

func WithTeamMemoryIDGen

func WithTeamMemoryIDGen(fn func() string) TeamMemoryServiceOption

type TeamMemorySettings

type TeamMemorySettings struct {
	CuratorAgents []string `json:"curator_agents"`
	Policy        string   `json:"policy"`
	UpdatedAt     string   `json:"updated_at,omitempty"`
	UpdatedBy     string   `json:"updated_by,omitempty"`
	Commit        string   `json:"commit,omitempty"`
	EffectHint    string   `json:"effect_hint,omitempty"`
}

type TeamMemorySnapshot

type TeamMemorySnapshot struct {
	TeamID           string
	Commit           string
	Entries          []MemoryItem
	Rules            []MemoryItem
	Proposals        []MemoryProposal
	Skipped          []string
	EffectHint       string
	RefreshSemantics string
}

type TrustedBootstrapCommand

type TrustedBootstrapCommand struct {
	ActorRef string
	Source   string
	Entries  []Entry
	Rules    []Rule
}

TrustedBootstrapCommand is the non-MCP/non-Web path used only for team instantiate and one-time legacy migration. It writes canonical memory without proposals, under the stable system Git author.

type UpdateTeamMemorySettingsInput

type UpdateTeamMemorySettingsInput struct {
	CuratorAgents []string
	Policy        string
	ActorRef      string
	ActorKind     string
	Author        Author
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL