trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
283 lines • 12.8 kB
TypeScript
/**
* TrellisVCS Type Definitions
*
* VCS-specific operation kinds, payloads, and entity types
* that extend the trellis-core kernel primitives.
*/
import type { Fact, Link } from '../core/store/eav-store.js';
export type VcsOpKind = 'vcs:fileAdd' | 'vcs:fileModify' | 'vcs:fileDelete' | 'vcs:fileRename' | 'vcs:dirAdd' | 'vcs:dirDelete' | 'vcs:branchCreate' | 'vcs:branchDelete' | 'vcs:branchAdvance' | 'vcs:milestoneCreate' | 'vcs:checkpointCreate' | 'vcs:merge' | 'vcs:symbolRename' | 'vcs:symbolMove' | 'vcs:symbolExtract' | 'vcs:signatureChange' | 'vcs:issueCreate' | 'vcs:issueUpdate' | 'vcs:issueStart' | 'vcs:issuePause' | 'vcs:issueResume' | 'vcs:issueClose' | 'vcs:issueReopen' | 'vcs:issueClaim' | 'vcs:issueClaimRelease' | 'vcs:criterionAdd' | 'vcs:criterionUpdate' | 'vcs:criterionRemove' | 'vcs:testRun' | 'vcs:zoneDefine' | 'vcs:zoneRename' | 'vcs:grantSet' | 'vcs:grantRetract' | 'vcs:issueBlock' | 'vcs:issueUnblock' | 'vcs:decisionRecord' | 'vcs:chatMessage' | 'vcs:laneCreate' | 'vcs:laneDrop' | 'vcs:laneGc' | 'vcs:lanePromoteStart' | 'vcs:lanePromoteComplete' | 'vcs:lanePromoteAbort' | 'vcs:remotePush' | 'vcs:remotePull' | 'vcs:gitSync' | 'vcs:repoAttest' | 'vcs:storeAssert' | 'vcs:storeRetract' | 'vcs:storeLink' | 'vcs:storeUnlink';
/**
* What kind of thing an issue is (ADR 0026).
*
* Bounded and enumerable on purpose: that is what lets `decompose` delete every
* prior value exhaustively (ADR 0022 §2's safe-register test) instead of relying
* on insertion order.
*/
export type IssueType = 'epic' | 'issue' | 'spike' | 'msg';
/** Every `IssueType`, for exhaustive delete-then-add in `decompose`. */
export declare const ISSUE_TYPES: readonly IssueType[];
export interface VcsPayload {
/**
* Who asserted this op and over what surface (ADR 0021 §2).
*
* Lives inside `vcs` deliberately: `hashVcsOp` hashes the `vcs` payload
* wholesale, so provenance is covered by the op hash with no preimage change
* and no migration — ops minted before this field still verify, since their
* payload simply lacks the key.
*/
provenance?: import('../core/persist/canonical-op.js').OpProvenance;
filePath?: string;
oldFilePath?: string;
contentHash?: string;
oldContentHash?: string;
size?: number;
language?: string;
branchName?: string;
targetOpHash?: string;
sourceBranch?: string;
baseBranch?: string;
baseOpHash?: string;
milestoneId?: string;
message?: string;
fromOpHash?: string;
toOpHash?: string;
trigger?: 'green-build' | 'interval' | 'op-count' | 'manual';
signature?: string;
signedBy?: string;
/** Device id that held the private key (`root` or paired deviceId). ADR 0020. */
signedWith?: string;
issueId?: string;
issueTitle?: string;
/**
* What KIND of thing this issue is (ADR 0026). `epic` is a container of
* intent; leaves roll up to one via `parentIssueId`, which is how an agent
* walks from a task to the reason it exists.
*
* A real field rather than an `Epic:` title prefix — the same lesson ADR 0022
* applied to zones: a name must not do the work of a type, or you cannot query
* it and a rename breaks it. Absent ⇒ `issue`.
*/
issueType?: IssueType;
/** Prior type, so a change is delete-then-add over a bounded domain. */
oldIssueType?: IssueType;
issueStatus?: 'backlog' | 'queue' | 'in_progress' | 'paused' | 'closed';
oldIssueStatus?: 'backlog' | 'queue' | 'in_progress' | 'paused' | 'closed';
issuePriority?: 'critical' | 'high' | 'medium' | 'low';
issueLabels?: string[];
parentIssueId?: string;
/** Previous parent when re-parenting or clearing via issueUpdate. */
oldParentIssueId?: string;
issueDescription?: string;
issueAssignee?: string;
pauseNote?: string;
blockedByIssueId?: string;
decisionId?: string;
decisionContext?: string;
decisionRationale?: string;
decisionAlternatives?: string;
decisionToolName?: string;
decisionToolInput?: string;
decisionToolOutput?: string;
chatMessageId?: string;
chatSessionId?: string;
chatLaneId?: string;
chatRole?: 'user' | 'assistant' | 'tool' | 'system';
chatText?: string;
chatToolName?: string;
/** Token usage carried on the message (input+output), for per-session/lane rollups. */
chatTokens?: number;
criterionId?: string;
criterionDescription?: string;
criterionCommand?: string;
/** Manifest suite id from `.trellis/tests.json` when command is omitted. */
criterionSuite?: string;
criterionStatus?: 'pending' | 'passed' | 'failed';
criterionOutput?: string;
testRunId?: string;
testRunSuite?: string;
testRunCommand?: string;
testRunStatus?: 'passed' | 'failed';
testRunOutput?: string;
testRunExitCode?: number;
testRunDurationMs?: number;
testRunTrigger?: 'manual' | 'watch' | 'pre-promote' | 'pre-close' | 'criterion';
remoteName?: string;
remoteRepoId?: string;
remoteTailHash?: string;
remoteByteLength?: number;
/** Commit hash minted by committing the working tree. */
gitCommitHash?: string;
/** Branch the working-tree commit landed on. */
gitBranch?: string;
/** Owner entity id (`identity:<did>`) self-attesting the ledger. */
repoOwner?: string;
/** Repo slug scoped under the owner (`{peer}/{repo}`). */
repoName?: string;
/** The ledger repoId the attestation covers. */
repoId?: string;
/** Project kind (code, knowledge-base, notes, data, media, other). */
projectKind?: string;
laneId?: string;
laneStatus?: 'active' | 'promoting' | 'promoted' | 'dropped';
gcDisposition?: string;
gcReason?: string;
targetBranch?: string;
parentLaneId?: string;
forkKind?: 'sibling' | 'child';
virtualBaseOpHash?: string;
sessionId?: string;
claimedLaneId?: string;
claimedSessionId?: string;
claimedAt?: string;
/** Immutable, authority-bearing zone id: `turtle://<ownerDid>/zone/<uuid>`. */
zoneId?: string;
/** Mutable human name. Renaming edits only this — never the id or grants. */
zoneAlias?: string;
/** Prior alias, so rename is delete-then-add rather than an append. */
oldZoneAlias?: string;
/** Opaque parent zoneId for nesting → grant inheritance closure. */
zoneParent?: string;
/** Level granted to anon (Reader = public, None = private). */
zoneDefaultVisibility?: number;
/** Principal a grant applies to (Agent Ed25519 did:key entity id). */
grantPrincipal?: string;
/** Granted level. `None` is never persisted — retraction removes the fact. */
grantLevel?: number;
/** Prior level, so a grant change is delete-then-add over a bounded domain. */
oldGrantLevel?: number;
facts?: Fact[];
links?: Link[];
}
/**
* A VcsOp mirrors KernelOp but widens `kind` to accept VCS-specific strings.
* We don't extend KernelOp directly because the kernel types `kind` as a
* narrow union; our VCS kinds are a superset.
*/
/**
* A VCS operation.
*
* ENVELOPE vs PAYLOAD (TRL-102). `hashVcsOp` hashes exactly
* `{kind, timestamp, agentId, previousHash, vcs}` — so `vcs` is the payload
* (identity-bearing) and any other top-level field is envelope (not hashed).
* That split was previously accidental; it is now deliberate. Do not add a
* top-level field expecting it to be covered by the hash — put it in `vcs`.
*/
export interface VcsOp {
hash: string;
kind: VcsOpKind | string;
timestamp: string;
agentId: string;
previousHash?: string;
vcs?: VcsPayload;
/**
* Envelope: local annotations attached to an op we did not mint. NOT hashed.
*
* `RemoteManager.prefixOp` tags pulled ops with `{e:'op', a:'remote', v:<name>}`
* so `trellis log --remote/--all` can filter them. Being outside the preimage
* is the point: we annotate another peer's op without invalidating its hash.
*
* Not to be confused with `vcs.facts`, which IS payload — that is where
* `vcs:storeAssert` puts graph facts, and it is hashed.
*/
facts?: import('../core/store/eav-store.js').Fact[];
links?: import('../core/store/eav-store.js').Link[];
/**
* Envelope: the lane this op was minted in. NOT hashed (TRL-102).
*
* Ambient context, not identity — the same semantic op minted in two lanes
* must produce the same hash, or peers lose dedup and cherry-picking across
* lanes rewrites op identity, which breaks set-reconciliation. Lane
* membership is also already implied by the journal the op lives in.
*
* Distinct from `vcs.laneId`, which is *subject* data for `vcs:laneCreate` /
* `vcs:laneDrop` / `vcs:testRun` — those ops are ABOUT a lane, pass laneId at
* mint, and must keep it inside the preimage or `laneCreate lane-A` would
* hash identically to `laneCreate lane-B`.
*/
laneId?: string;
}
export interface FileChangeEvent {
type: 'add' | 'modify' | 'delete' | 'rename';
path: string;
oldPath?: string;
contentHash?: string;
oldContentHash?: string;
size?: number;
timestamp: string;
}
/**
* Writer identity for an op: the signed Ed25519 principal, falling back to the
* self-asserted `agentId` for unsigned ops (ADR 0022 §4).
*
* Lives here, not in `branch.ts`, because it is a pure function of the op and
* `decompose` needs it. `branch.ts` imports `fs`, so importing this from there
* dragged Node's `fs`/`path` into every consumer of `decompose` — including a
* browser peer, which cannot bundle it at all.
*/
export declare function writerPrincipal(op: VcsOp): string;
/**
* Entity id of a per-writer branch head ("ref zone", ADR 0022 §4). `integration`
* and the default branch collapse to the shared `branch:NAME`; every other
* branch gets `branch:NAME@<principal>` so two writers never share a pointer.
*/
export declare function branchHeadEntity(branchName: string, principal?: string, defaultBranch?: string): string;
export declare function fileEntityId(path: string): string;
export declare function dirEntityId(path: string): string;
export declare function branchEntityId(name: string): string;
export declare function milestoneEntityId(hash: string): string;
export declare function checkpointEntityId(hash: string): string;
export declare function issueEntityId(id: string): string;
export declare function criterionEntityId(issueId: string, index: number): string;
export declare function testRunEntityId(id: string): string;
export declare function decisionEntityId(id: string): string;
export declare function laneEntityId(id: string): string;
export interface TrellisVcsConfig {
/** Absolute path to the repository root. */
rootPath: string;
/** Glob patterns to ignore (e.g. ['node_modules', '.git', '*.log']). */
ignorePatterns: string[];
/** Debounce interval for file watcher in ms. */
debounceMs: number;
/** Name of the default branch. */
defaultBranch: string;
/** Path to the .trellis database file. */
dbPath: string;
/** Whether init/watch should reconcile existing workspace files by default. */
indexWorkspace: boolean;
/** Stable ledger identity (ADR 0031) — independent of checkout path. */
repoId?: string;
/** Project metadata (ADR 0032 §2/§5) — owner identity + slug + kind. */
project?: {
/** Owner entity id (`identity:<did>`) — the person who created it. */
owner?: string;
/** Repo slug scoped under the owner (`{peer}/{repo}`). */
name?: string;
/** Project kind (code, knowledge-base, notes, data, media, other). */
kind?: string;
};
/** Agent lane filesystem bind (ADR 0014 Phase 2). */
lanes?: {
/** Provision git worktrees per lane; default true on init. */
worktreeBind?: boolean;
/** Auto-prune worktrees after N days of inactivity (default: 7). */
worktreeRetentionDays?: number;
};
/** Git mirror adapter — sync integration to main at promote/close. */
git?: {
/** Auto-commit on lane promote (default true on init). */
syncOnPromote?: boolean;
/** Push on issue close when --push or this flag is set. */
pushOnClose?: boolean;
/** Remote name for push (default origin). */
remote?: string;
/** Branch to commit on (default defaultBranch). */
branch?: string;
};
/** Milestone → git commit automation. */
milestones?: {
/** Auto-commit integration to git (with the milestone message) on create. */
autoCommit?: boolean;
};
}
export declare const DEFAULT_CONFIG: Omit<TrellisVcsConfig, 'rootPath'>;
//# sourceMappingURL=types.d.ts.map