Documentation
¶
Overview ¶
Package streams owns the identity of a dependency stream: one named cross-repository unit of work spanning a library and the consumers that must change with it.
A stream introduces no second identity for the same work. Its name is the WB worktree task name, its worktrees are created by the existing worktree creation path, and its fleet-wide claim is the one `wb worktree create` already takes. What this package adds is the durable record that ties those per-repository checkouts together — membership, roles, the `stream/<name>` branch and its draft pull request, the branch lease, and every live local link — so `status`, `sync`, `propagate` and `end` can act on the set rather than on one repository at a time.
The record lives under WB's home directory, never inside a member repository: `stream-state-is-untracked-and-local` makes that a requirement rather than a convenience, so a stream survives an interrupted session and `git status` in every member stays clean.
Implements: dependency-streams#req:stream-is-a-named-set-of-worktrees, dependency-streams#req:stream-state-is-untracked-and-local.
Index ¶
- Constants
- Variables
- func Branch(name string) string
- func GoWorkUseEntries(worktree string) ([]string, error)
- func IsStreamBranch(ref string) bool
- func ParseGoWorkUseEntries(contents string) []string
- func PreflightChecks() []string
- func RedactString(value string) string
- func ValidateName(name string) error
- func ValidateRepository(repository string) error
- type AgentPullRequest
- type AgentPullRequestOutcome
- type Commit
- type ConsumerBehind
- type CreatedWorktree
- type Declaration
- type DiscardEvents
- type Ecosystem
- type EndMemberResult
- type EndOptions
- type EndResult
- type Engine
- func (engine *Engine) End(ctx context.Context, options EndOptions) (EndResult, error)
- func (engine *Engine) Join(ctx context.Context, options JoinOptions) (StartResult, error)
- func (engine *Engine) Start(ctx context.Context, options StartOptions, transitive []string) (StartResult, error)
- func (engine *Engine) Status(ctx context.Context, name string) (Status, error)
- type Event
- type EventAppender
- type ExecGit
- func (git ExecGit) CommitsNotIn(ctx context.Context, dir, branch, base string) ([]Commit, error)
- func (git ExecGit) CurrentBranch(ctx context.Context, dir string) (string, error)
- func (git ExecGit) DefaultBranch(ctx context.Context, dir string) (string, error)
- func (git ExecGit) DeleteRemoteBranch(ctx context.Context, dir, branch, expectedSHA string) error
- func (git ExecGit) DirtyPaths(ctx context.Context, dir string) ([]string, error)
- func (git ExecGit) Fetch(ctx context.Context, dir string) error
- func (git ExecGit) IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error)
- func (git ExecGit) LocalBranchHead(ctx context.Context, dir, branch string) (string, bool, error)
- func (git ExecGit) LocalHead(ctx context.Context, dir string) (string, error)
- func (git ExecGit) LogSubjects(ctx context.Context, dir, from, to string) ([]string, error)
- func (git ExecGit) PushBranch(ctx context.Context, dir, branch string) (string, error)
- func (git ExecGit) RemoteHead(ctx context.Context, dir, branch string) (string, bool, error)
- func (git ExecGit) Tags(ctx context.Context, dir, pattern string) ([]string, error)
- type ExecGitHub
- func (hub ExecGitHub) ClosePullRequest(ctx context.Context, dir string, number int, comment string) error
- func (hub ExecGitHub) CreateDraftPullRequest(ctx context.Context, dir, base, head, title, body string) (PullRequest, error)
- func (hub ExecGitHub) DefaultBranchStatus(ctx context.Context, dir, branch string) (string, error)
- func (hub ExecGitHub) OpenPullRequestsTargeting(ctx context.Context, dir, base string) ([]PullRequest, error)
- func (hub ExecGitHub) PullRequest(ctx context.Context, dir string, number int) (PullRequest, bool, error)
- func (hub ExecGitHub) PullRequestForBranch(ctx context.Context, dir, branch string) (PullRequest, bool, error)
- func (hub ExecGitHub) RetargetPullRequest(ctx context.Context, dir string, number int, base string) error
- func (hub ExecGitHub) UpdatePullRequestTitle(ctx context.Context, dir string, number int, title string) error
- type FileEventLog
- type Git
- type GitHub
- type GoModule
- type HooksChecker
- type Identity
- type JoinOptions
- type Lease
- type Link
- type LinkState
- type LinkedConsumer
- type LinkedConsumerBinding
- type Mechanism
- type Member
- type MemberLink
- type MemberStatus
- type MergedUntagged
- type Phase
- type Preflight
- type PreflightFinding
- type PreflightInput
- type PreflightStatus
- type PublicationFailure
- type PullRequest
- type Refusal
- type Role
- type SquashAbsorptionReceipt
- type StartOptions
- type StartResult
- type Status
- type Store
- func (store *Store) Archive(name string) (string, error)
- func (store *Store) ArchiveLocked(name string) (string, error)
- func (store *Store) Create(stream Stream) (Stream, error)
- func (store *Store) CreateLocked(stream Stream) (Stream, error)
- func (store *Store) Delete(name string) error
- func (store *Store) Dir(name string) string
- func (store *Store) EventLog(name string) *FileEventLog
- func (store *Store) LinkSourcesForWorktree(worktree string) ([]StreamLinkSource, error)
- func (store *Store) List() ([]Stream, []Unreadable, error)
- func (store *Store) LiveLinksForWorktree(worktree string) ([]StreamLink, error)
- func (store *Store) Load(name string) (Stream, error)
- func (store *Store) RepositoryStream(repository string) (Stream, bool, []Unreadable, error)
- func (store *Store) Update(name string, mutate func(*Stream) error) (Stream, error)
- func (store *Store) WithStoreLock(body func() error) error
- type Stream
- func (stream Stream) Consumers() []Member
- func (stream Stream) Library() (Member, bool)
- func (stream Stream) Lifecycle() Phase
- func (stream Stream) LinkedConsumer(repository string) (LinkedConsumerBinding, bool)
- func (stream Stream) LiveLinks() []MemberLink
- func (stream Stream) Member(repository string) (Member, bool)
- func (stream Stream) Open() bool
- type StreamLink
- type StreamLinkSource
- type Unreadable
- type WorktreeCreator
Constants ¶
const ( GoWorkFile = "go.work" GoWorkSum = "go.work.sum" )
GoWorkFile and GoWorkSum are the two untracked files a Go local link creates. They are named here because more than one verb has to recognise them: the link creates them, and both `wb worktree merge` and `wb worktree end` refuse a worktree that still carries one.
const ( CheckHooks = "hooks-check" CheckNpmProviderIdentity = "npm-provider-identity" CheckRedMain = "red-main" CheckStreamConcurrency = "stream-pr-concurrency" )
Check names every preflight check WB runs, in the order it runs them. `verbs-state-and-deduplicate-their-work` requires a verb to say what it will run before running it, so the list is data rather than control flow.
const ( RefusalStreamExists = "stream-exists" RefusalRepositoryInStream = "repository-in-stream" RefusalPreflight = "preflight-failed" // RefusalNoLibrary fires when a stream has no library member at all. RefusalNoLibrary = "no-library" // RefusalLibraryExists fires when a second library is proposed for a // stream that already has one. It used to share RefusalNoLibrary's code, // which said the opposite of what happened — and refusal codes are // contract that skills branch on. RefusalLibraryExists = "library-exists" // RefusalUsage marks an ambiguous invocation. It exists so a usage error // carries the same envelope and exit code as any other guard. RefusalUsage = "usage" RefusalLiveLink = "live-link" RefusalUnabsorbedWork = "unabsorbed-work" RefusalStreamEnded = "stream-ended" )
Refusal codes are contract: they appear in the JSON envelope and skills branch on them.
const BranchPrefix = streambranch.Prefix
BranchPrefix is the namespace every stream branch lives in. It is also what the push hook keys its "CI on the stream pull request is the gate" decision on, so the definition lives in the shared leaf package both sides import.
const EventSchemaVersion = 1
EventSchemaVersion is the stream event-log format this binary writes.
const SchemaVersion = 2
SchemaVersion is the stream-state format this binary writes and the newest it can read. A newer file is refused rather than silently misread: stream state carries live links whose reversal detail is the only record of the published versions a consumer had before linking.
Variables ¶
var ErrNotFound = errors.New("stream not found")
ErrNotFound is returned when no stream with that name exists.
Functions ¶
func GoWorkUseEntries ¶
GoWorkUseEntries reads the `use` entries of a worktree's go.work, if any.
This is the file-based half of `merge-refuses-a-linked-worktree`, and it is deliberately independent of stream state: state alone would miss a hand-written workspace, so the refusal reads the file too.
func IsStreamBranch ¶
IsStreamBranch reports whether a branch name — or a full `refs/heads/…` ref — is inside the stream namespace.
func ParseGoWorkUseEntries ¶
ParseGoWorkUseEntries reads both spellings Go accepts — a bare `use <dir>` and a `use (...)` block — and skips comments, so a commented-out entry is never read as a live link.
func PreflightChecks ¶
func PreflightChecks() []string
PreflightChecks is the declared plan, in run order.
func RedactString ¶
RedactString removes credential-shaped substrings.
func ValidateName ¶
ValidateName refuses a stream name that could not also be a worktree task name, before anything durable is created.
func ValidateRepository ¶
ValidateRepository refuses anything that is not an owner/repository slug.
Types ¶
type AgentPullRequest ¶
type AgentPullRequest struct {
Repository string `json:"repository"`
Number int `json:"number"`
URL string `json:"url"`
Title string `json:"title"`
Head string `json:"head"`
}
AgentPullRequest is one open pull request against the stream branch.
type AgentPullRequestOutcome ¶
type AgentPullRequestOutcome struct {
Repository string `json:"repository"`
Number int `json:"number"`
URL string `json:"url"`
Action string `json:"action"`
Detail string `json:"detail,omitempty"`
}
AgentPullRequestOutcome is one agent pull request's disposition.
type Commit ¶
type Commit struct {
SHA string `json:"sha"`
Subject string `json:"subject"`
// PatchID is `git patch-id --stable` over the commit's diff. It is empty
// only when Git could not produce one (an empty commit, for example), and
// an empty value is never treated as equal to another empty value.
PatchID string `json:"patch_id,omitempty"`
}
Commit is one commit on a stream branch, identified by its patch rather than only by its SHA.
A rebase-and-merge landing rewrites SHAs by construction, and one body of work re-applied on two branches has two SHAs and one patch. Clustering by patch identity — Git's own `patch-id --stable` — is what lets `stream-backlog-is-counted-by-patch-identity` name N branches carrying one change as one item. Subject text is a label, never the identity.
type ConsumerBehind ¶
type ConsumerBehind struct {
Repository string `json:"repository"`
Identity string `json:"identity"`
Manifest string `json:"manifest"`
Declared string `json:"declared"`
Published string `json:"published"`
}
ConsumerBehind is gap three.
type CreatedWorktree ¶
type CreatedWorktree struct {
Repository string
Worktree string
Canonical string
Branch string
Base string
}
CreatedWorktree is one checkout the worktree creation path published.
type Declaration ¶
type Declaration struct {
Identity Identity `json:"identity"`
// Manifest is the consumer-relative manifest that declares it.
Manifest string `json:"manifest"`
// Version is exactly as declared, including any range prefix.
Version string `json:"version"`
// Section is the canonical dependency section the declaration came from.
Section string `json:"section,omitempty"`
// Workspace is the consumer-relative npm workspace that owns Manifest.
Workspace string `json:"workspace,omitempty"`
}
Declaration is one consumer's declared dependency on a library identity.
func DiscoverDeclarations ¶
func DiscoverDeclarations(root string, identities []Identity) ([]Declaration, error)
DiscoverDeclarations finds every declaration a consumer worktree makes of the given library identities. A consumer that declares none of them is not linkable, and reporting that is the point: it must be skipped rather than linked to something it does not use.
type DiscardEvents ¶
type DiscardEvents struct{}
DiscardEvents is an appender that records nothing. It exists so a verb can run in a context with no stream directory (a dry preflight, a unit test) without special-casing a nil appender at every call site.
func (DiscardEvents) Append ¶
func (DiscardEvents) Append(Event) error
Append implements EventAppender.
type EndMemberResult ¶
type EndMemberResult struct {
Repository string `json:"repository"`
Worktree string `json:"worktree"`
// RemoteBranchDeleted records that origin/stream/<name> is gone.
RemoteBranchDeleted bool `json:"remote_branch_deleted"`
// WorktreeRemoved is true when the existing cleanup path retired it.
WorktreeRemoved bool `json:"worktree_removed"`
// LeaseReleased is true when the stream lease record was cleared.
LeaseReleased bool `json:"lease_released"`
// DraftPullRequest is the member's own stream pull request, reported
// rather than merged: ending a stream publishes, bumps and merges
// nothing.
DraftPullRequest int `json:"draft_pull_request,omitempty"`
DraftAction string `json:"draft_action,omitempty"`
Detail string `json:"detail,omitempty"`
}
EndMemberResult is one member's retirement.
type EndOptions ¶
type EndOptions struct {
Name string
// Apply performs the removal. Without it the verb reports exactly what it
// would do and changes nothing, so an operator can see which agent pull
// requests would be closed before any of them are.
Apply bool
// Retarget moves still-open agent pull requests onto the member's base
// instead of closing them. The default closes them, because GitHub's own
// silent retarget is the hazard this verb exists to prevent and doing it
// deliberately must be an explicit choice.
Retarget bool
// ForceUnabsorbed proceeds past the absorption guard. It is not a bypass
// that hides anything: it requires Reason, records both in the event log,
// and names every commit it is stepping over. Without it, an absorption
// check WB could not run refuses — a check that cannot answer must not
// pass.
ForceUnabsorbed bool
// Reason is mandatory with ForceUnabsorbed and is recorded verbatim.
Reason string
// KeepRemoteBranch leaves origin/stream/<name> in place. By default end
// deletes it, because leaving it is scaffolding the verb claims to
// remove.
KeepRemoteBranch bool
}
EndOptions is one `wb stream end` invocation.
type EndResult ¶
type EndResult struct {
Stream string `json:"stream"`
Applied bool `json:"applied"`
Members []EndMemberResult `json:"members"`
// AgentPullRequests records what happened to every still-open pull
// request that targeted a stream branch.
AgentPullRequests []AgentPullRequestOutcome `json:"agent_pull_requests"`
Errors []string `json:"errors,omitempty"`
// Forced names every finding `--force-unabsorbed` stepped over, and
// ForcedReason the operator's recorded justification. Both are also
// written to the event log, so the decision is auditable after the fact.
Forced []string `json:"forced,omitempty"`
ForcedReason string `json:"forced_reason,omitempty"`
}
EndResult is what `stream end` did, or would do.
type Engine ¶
type Engine struct {
Store *Store
Git Git
GitHub GitHub
Worktrees WorktreeCreator
// ProjectsRoot locates the canonical clones the preflight checks read.
ProjectsRoot string
// HooksCheck answers the readiness preflight's hooks question. Nil uses
// the installed `wb hooks check`.
HooksCheck HooksChecker
// Login, Machine and Session identify the lease holder.
Login string
Machine string
Session string
Now func() time.Time
}
Engine runs the stream verbs against injected ports.
func (*Engine) End ¶
End removes a stream's own scaffolding and restores published state.
It refuses while any live local link remains, because a stream that ended with a consumer still resolving an unpublished working tree has not restored published state at all; and it refuses a member whose branch carries work the base has not absorbed, by patch identity rather than by path listing. Before a stream branch could be deleted it enumerates every still-open pull request targeting it and closes or retargets each, because GitHub silently retargets such a pull request at the base when its base branch disappears — producing the misrouted pull request this Feature guards against with no operator mistake to blame.
Ending publishes, bumps and merges nothing.
Implements: dependency-streams#req:stream-end-proves-absorption-and-removes-its-own-scaffolding, dependency-streams#req:stream-end-removes-every-stream-worktree.
`stream-end-restores-published-state` is only PARTLY implemented here. The REQ says end must remove every live link before delegating worktree removal; this verb refuses while one remains and names the exact command per link, which is a guard rather than the removal. Removing them inside `end` needs the local-link verb that lands in the propagate-local row — calling it from here would be a forward dependency on code this row does not contain. Until then, `one-verb-per-operation` is satisfied only for the refusal, and the operator still chains the undos by hand. That gap is deliberate and tracked, not an oversight.
func (*Engine) Join ¶
func (engine *Engine) Join(ctx context.Context, options JoinOptions) (StartResult, error)
Join adds a repository to an existing stream, creating its stream worktree, branch and draft pull request exactly as Start does, so every later verb treats it as a member from that point on.
Implements: dependency-streams#req:stream-pushes-use-a-lease-and-a-stream-claim (the join half of the one-stream-per-repository refusal).
func (*Engine) Start ¶
func (engine *Engine) Start(ctx context.Context, options StartOptions, transitive []string) (StartResult, error)
Start creates a stream. Every fence runs before the first side effect: name validation, the one-open-stream-per-repository claim, and the fleet readiness checks all complete before any worktree is created.
Implements: dependency-streams#req:stream-is-a-named-set-of-worktrees, dependency-streams#req:stream-state-is-untracked-and-local, dependency-streams#req:stream-branch-with-draft-pr, dependency-streams#req:stream-start-proves-the-fleet-is-ready, dependency-streams#req:stream-pushes-use-a-lease-and-a-stream-claim.
type Event ¶
type Event struct {
SchemaVersion int `json:"schema_version"`
Timestamp time.Time `json:"timestamp"`
Stream string `json:"stream"`
Verb string `json:"verb"`
// Phase is the step inside the verb — "preflight", "worktree",
// "pull-request", "link", "unlink", "lease" — so a timeline can show
// where a verb spent its time rather than only that it ran.
Phase string `json:"phase,omitempty"`
// Repository qualifies the event when it is about one member.
Repository string `json:"repository,omitempty"`
// Outcome is the envelope vocabulary: success, findings, refused.
Outcome string `json:"outcome"`
// RefusalCode is set only when Outcome is "refused". It is the stable
// identifier a caller branches on.
RefusalCode string `json:"refusal_code,omitempty"`
// Detail is human-readable and redacted.
Detail string `json:"detail,omitempty"`
// DurationMS is the wall time of the phase, when the verb measured it.
DurationMS int64 `json:"duration_ms,omitempty"`
// Evidence carries the exact facts the verb relied on. Values are
// redacted before the event is written.
Evidence map[string]string `json:"evidence,omitempty"`
// Provenance fields (wb#631, SDLC logging-gap analysis 2026-09-18): IDs
// only, stamped by Append from the environment at zero cost, never a
// prompt or response body. Additive and omitempty: a reader older than
// this change simply never sees them, and EventSchemaVersion did not
// need to move for a purely additive field.
HarnessSessionID string `json:"harness_session_id,omitempty"`
Harness string `json:"harness,omitempty"`
EffortLevel string `json:"effort_level,omitempty"`
AgentID string `json:"agent_id,omitempty"`
ToolUseID string `json:"tool_use_id,omitempty"`
WBVersion string `json:"wb_version,omitempty"`
}
Event is one structured record appended by a stream verb.
`every-verb-appends-a-structured-event` is the requirement; the fields here are the minimum every P0 verb can populate truthfully. The log is append-only, versioned, redacted before any bytes are written, and safe for concurrent appenders.
func ReadEvents ¶
ReadEvents parses one stream's event log. A record from a newer schema is refused rather than partially interpreted.
type EventAppender ¶
EventAppender is the seam between a stream verb and the event log.
LANE SEAM: the P0 delivery order puts the shared event-log implementation in the row that also owns the exit-code/JSON envelope contract. Stream verbs depend on this interface, never on a concrete writer, so the shared implementation can replace FileEventLog without touching a verb. Nothing below the interface is part of a verb's contract.
type ExecGit ¶
type ExecGit struct {
// Timeout bounds each child. Zero uses defaultCommandTimeout.
Timeout time.Duration
}
ExecGit runs real Git.
func (ExecGit) CommitsNotIn ¶
CommitsNotIn implements Git by patch identity.
`git cherry` answers which commits base does not already carry *as patches*, which is the right question after a rebase-and-merge landing rewrites SHAs. It answers with SHAs; the patch ids come from `git patch-id --stable` over the same range, so two commits carrying one body of work on two branches are recognisably one patch even though their SHAs differ by construction.
Subject text is carried as a label only. Keying on it would cluster two unrelated commits that happen to share a message, and would fail to cluster one change re-applied with an edited message.
func (ExecGit) CurrentBranch ¶
CurrentBranch implements Git.
func (ExecGit) DefaultBranch ¶
DefaultBranch implements Git using only local state.
func (ExecGit) DeleteRemoteBranch ¶
DeleteRemoteBranch implements Git and asserts the effect: after the push that deletes the ref, origin must no longer resolve it.
func (ExecGit) DirtyPaths ¶
DirtyPaths implements Git.
func (ExecGit) IsAncestor ¶ added in v0.127.3
IsAncestor implements Git without treating patch-equivalence as commit identity. A squash-merged stream PR has immutable GitHub identities, so only a local head on that PR's ancestry can prove it has no later work.
func (ExecGit) LocalBranchHead ¶ added in v0.127.3
LocalBranchHead implements Git without falling back to a remote-tracking ref. A recovered stream member has no worktree to inspect, but its canonical clone can still hold an unpushed stream branch.
func (ExecGit) LogSubjects ¶
LogSubjects implements Git.
func (ExecGit) PushBranch ¶
PushBranch implements Git and verifies the ref it pushed.
`push-verifies-the-ref-it-pushed`: the push exit code is not evidence the intended commit landed, so the local and remote SHAs are compared after the push and a divergence is an error.
func (ExecGit) RemoteHead ¶
RemoteHead implements Git.
type ExecGitHub ¶
ExecGitHub runs the installed `gh`.
Every call uses `gh api` or a `--json` read rather than parsing human output: `land-verbs-work-with-the-installed-gh` records what happens when a verb depends on the presentation of a specific gh release.
func (ExecGitHub) ClosePullRequest ¶
func (hub ExecGitHub) ClosePullRequest(ctx context.Context, dir string, number int, comment string) error
ClosePullRequest implements GitHub and asserts the effect.
`verbs-assert-effects-not-exit-codes` is a Principle-level MUST, and this is a destructive call whose result lands in a durable report: a `gh pr close` that exits 0 without closing — a permission edge, a race with a merge — must not be recorded as done.
func (ExecGitHub) CreateDraftPullRequest ¶
func (hub ExecGitHub) CreateDraftPullRequest(ctx context.Context, dir, base, head, title, body string) (PullRequest, error)
CreateDraftPullRequest implements GitHub.
func (ExecGitHub) DefaultBranchStatus ¶
DefaultBranchStatus implements GitHub. An unresolvable status is reported as an empty conclusion rather than an error: a red-`main` check that cannot run must be reported as unknown, never assumed green.
func (ExecGitHub) OpenPullRequestsTargeting ¶
func (hub ExecGitHub) OpenPullRequestsTargeting(ctx context.Context, dir, base string) ([]PullRequest, error)
OpenPullRequestsTargeting implements GitHub.
func (ExecGitHub) PullRequest ¶
func (hub ExecGitHub) PullRequest(ctx context.Context, dir string, number int) (PullRequest, bool, error)
PullRequest implements GitHub.
func (ExecGitHub) PullRequestForBranch ¶
func (hub ExecGitHub) PullRequestForBranch(ctx context.Context, dir, branch string) (PullRequest, bool, error)
PullRequestForBranch implements GitHub.
func (ExecGitHub) RetargetPullRequest ¶
func (hub ExecGitHub) RetargetPullRequest(ctx context.Context, dir string, number int, base string) error
RetargetPullRequest implements GitHub and asserts the effect.
func (ExecGitHub) UpdatePullRequestTitle ¶ added in v0.129.0
func (hub ExecGitHub) UpdatePullRequestTitle(ctx context.Context, dir string, number int, title string) error
UpdatePullRequestTitle implements GitHub and asserts the effect.
type FileEventLog ¶
FileEventLog is the append-only JSONL log beside a stream's state.
Concurrent appends are safe: each append takes an exclusive lock on the log and writes one already-encoded line, so two verbs running at once interleave records rather than corrupt one.
func (*FileEventLog) Append ¶
func (log *FileEventLog) Append(event Event) error
Append writes one redacted event. A failure to record an event never fails the verb's own work — callers log it — but it is reported rather than swallowed here.
type Git ¶
type Git interface {
// CurrentBranch reports the checked-out branch of a worktree.
CurrentBranch(ctx context.Context, dir string) (string, error)
// DefaultBranch reports the repository's default branch from local
// state. It must never contact the network.
DefaultBranch(ctx context.Context, dir string) (string, error)
// Fetch refreshes origin so a verb acts on a live view rather than a
// session-start snapshot.
Fetch(ctx context.Context, dir string) error
// PushBranch publishes branch from dir and sets its upstream. It must
// verify the pushed ref rather than trust the exit code.
PushBranch(ctx context.Context, dir, branch string) (sha string, err error)
// RemoteHead resolves origin/<branch>, reporting ok=false when the
// branch does not exist on the remote.
RemoteHead(ctx context.Context, dir, branch string) (sha string, ok bool, err error)
// LocalHead resolves the worktree's HEAD.
LocalHead(ctx context.Context, dir string) (string, error)
// LocalBranchHead resolves a local branch without consulting origin. It is
// used when a stream worktree has already disappeared: a surviving local
// stream ref can still carry unpushed work that must block retirement.
LocalBranchHead(ctx context.Context, dir, branch string) (sha string, ok bool, err error)
// IsAncestor reports whether ancestor is reachable from descendant. Stream
// cleanup uses commit ancestry, not patch similarity, when a merged PR is
// the sole receipt that authorizes retiring a squash-merged member.
IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error)
// CommitsNotIn lists the commits on branch whose patch base does not
// already carry, by patch identity rather than by SHA — a rebase landing
// rewrites SHAs, so an ancestry test would refuse every landed stream
// forever. Each commit carries its `git patch-id --stable`, which is what
// clusters N branches carrying one body of work into one item.
CommitsNotIn(ctx context.Context, dir, branch, base string) ([]Commit, error)
// DirtyPaths lists modified or untracked paths in a worktree.
DirtyPaths(ctx context.Context, dir string) ([]string, error)
// Tags lists tags matching a glob pattern, newest version first.
Tags(ctx context.Context, dir, pattern string) ([]string, error)
// LogSubjects lists the subjects of commits in the exclusive range
// from..to. An empty from means "every commit reachable from to".
LogSubjects(ctx context.Context, dir, from, to string) ([]string, error)
// DeleteRemoteBranch removes a branch from origin only if it remains at
// expectedSHA, then verifies it is gone. The lease closes the interval
// between stream proof and deletion: a later remote write must refuse, not
// be erased as stream scaffolding.
DeleteRemoteBranch(ctx context.Context, dir, branch, expectedSHA string) error
}
Git is the local Git surface stream verbs use.
type GitHub ¶
type GitHub interface {
// CreateDraftPullRequest opens a draft pull request from head to base.
CreateDraftPullRequest(ctx context.Context, dir, base, head, title, body string) (PullRequest, error)
// UpdatePullRequestTitle changes one pull request's title and verifies the
// remote effect before returning success.
UpdatePullRequestTitle(ctx context.Context, dir string, number int, title string) error
// PullRequestForBranch finds the open pull request whose head is branch.
PullRequestForBranch(ctx context.Context, dir, branch string) (PullRequest, bool, error)
// OpenPullRequestsTargeting lists every open pull request whose base is
// base. `stream end` needs this to find the agent pull requests that
// GitHub would silently retarget at `main` when the stream branch is
// deleted.
OpenPullRequestsTargeting(ctx context.Context, dir, base string) ([]PullRequest, error)
// ClosePullRequest closes one pull request with a comment saying why.
ClosePullRequest(ctx context.Context, dir string, number int, comment string) error
// RetargetPullRequest moves one pull request onto a new base.
RetargetPullRequest(ctx context.Context, dir string, number int, base string) error
// PullRequest re-reads one pull request by number, reporting found=false
// when it does not resolve. It is what makes a close or a retarget an
// asserted effect rather than a trusted exit code.
PullRequest(ctx context.Context, dir string, number int) (PullRequest, bool, error)
// DefaultBranchStatus reports the conclusion of the most recent
// completed CI run on the repository's default branch: "success",
// "failure", or "" when it cannot be established.
DefaultBranchStatus(ctx context.Context, dir, branch string) (conclusion string, err error)
}
GitHub is the remote surface stream verbs use. Every method takes the worktree directory so the underlying tool resolves the repository the same way an operator standing in that directory would.
type GoModule ¶
type GoModule struct {
// Path is the module path declared by the manifest.
Path string `json:"path"`
// Manifest is the worktree-relative go.mod path.
Manifest string `json:"manifest"`
// Directory is the worktree-relative module directory, which is what a
// workspace `use` entry names. It is "." for a module at the root.
Directory string `json:"directory"`
}
GoModule is one Go module inside a worktree.
func GoModules ¶
GoModules lists every Go module in a worktree: `backend/`, the module root where there is no `backend/`, and any nested tooling module.
A workspace containing only the library would leave the consumer's own module outside the workspace it now sits under, and `go build ./...` in `backend/` would not resolve at all — which is why this enumerates the whole worktree rather than a conventional pair of paths.
Implements: dependency-streams#req:go-consumers-link-through-an-untracked-go-work.
type HooksChecker ¶
HooksChecker answers "are this checkout's WB-managed hooks healthy" for the readiness preflight. It is a port so the refusal it drives is provable without installing real hooks into a temporary repository.
func InstalledHooksChecker ¶
func InstalledHooksChecker(wbExecutable, projectsRoot string) HooksChecker
InstalledHooksChecker is the production checker: the existing `wb hooks check`, called in process.
type Identity ¶
type Identity struct {
Ecosystem Ecosystem `json:"ecosystem"`
Name string `json:"name"`
// Manifest is the worktree-relative path that declares the identity.
Manifest string `json:"manifest"`
// Directory is the worktree-relative directory of the manifest. For a Go
// module it is the directory a workspace `use` entry names.
Directory string `json:"directory"`
// Workspace is the worktree-relative npm workspace that owns Manifest.
// It is "." for packages in a repository-root workspace.
Workspace string `json:"workspace,omitempty"`
}
Identity is one published identity of a library: the name a consumer resolves, and the manifest that declares it.
func DiscoverPublished ¶
DiscoverPublished reads the identities a library worktree publishes.
Discovery is evidence-based and reads the library worktree itself: the Go module path from `backend/go.mod`, or from the module root where the repository has no `backend/`, and npm package names from `libs/**/package.json` in every repository-owned npm workspace (including nested frontend/). An operator-supplied package name is never accepted as a substitute, because the whole value of a local link is that it exposes what the library actually publishes.
Implements: dependency-streams#req:local-link-discovers-what-the-library-publishes.
type JoinOptions ¶
JoinOptions is one `wb stream join` invocation.
type Lease ¶
type Lease struct {
Login string `json:"login,omitempty"`
Machine string `json:"machine,omitempty"`
Session string `json:"session,omitempty"`
// RecordedHead is the stream head WB last observed on the remote. It is
// what `wb stream sync` passes to `--force-with-lease`; it is empty
// until a stream push records one.
RecordedHead string `json:"recorded_head,omitempty"`
HeldSince time.Time `json:"held_since"`
}
Lease is the stream's claim on `stream/<name>` in one repository. It records the live registered session as well as the machine, because `claims-carry-a-session-identity` makes a push from a different live session on the same machine a refusal rather than a silent overwrite.
type Link ¶
type Link struct {
// Library is the library worktree the link points at. It may no longer
// exist when the link is undone; that is deliberate.
Library string `json:"library"`
// LibraryRepository names the owning repository, which survives the
// worktree being removed.
LibraryRepository string `json:"library_repository"`
Mechanism Mechanism `json:"mechanism"`
State LinkState `json:"state,omitempty"`
// Identity is the published identity being replaced: a Go module path or
// an npm package name.
Identity string `json:"identity"`
// PreviousVersion is the version the consumer declared before linking.
// It is what `--undo` restores.
PreviousVersion string `json:"previous_version,omitempty"`
// ContentHash identifies the library working tree, including modified and
// untracked files, that this link exposed. The library is uncommitted by
// construction, so it has no SHA.
ContentHash string `json:"content_hash,omitempty"`
// Artifacts are the untracked paths the link created, relative to the
// consumer worktree, so removal never guesses.
Artifacts []string `json:"artifacts,omitempty"`
// Workspace is the consumer-relative npm workspace containing the link.
// Empty and "." both mean the repository root for backward compatibility.
Workspace string `json:"workspace,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
Link is one live local link from a consumer worktree to a library worktree. It carries everything needed to reverse the link exactly, so `--undo` depends on the record rather than on the library worktree still existing.
type LinkState ¶ added in v0.110.0
type LinkState string
LinkState distinguishes a record written before its filesystem operation from one whose operation completed. Empty is a legacy applied record.
type LinkedConsumer ¶
type LinkedConsumer struct {
Repository string `json:"repository"`
Worktree string `json:"worktree"`
Library string `json:"library"`
Mechanism Mechanism `json:"mechanism"`
Identity string `json:"identity"`
PreviousVersion string `json:"previous_version,omitempty"`
ContentHash string `json:"content_hash,omitempty"`
}
LinkedConsumer is gap one.
type LinkedConsumerBinding ¶ added in v0.117.3
type Mechanism ¶
type Mechanism string
Mechanism names how one live local link replaces a published dependency.
type Member ¶
type Member struct {
Repository string `json:"repository"`
Role Role `json:"role"`
// Worktree is the checkout `wb worktree create` published for this
// member. It is the path every later verb acts in.
Worktree string `json:"worktree"`
// Canonical is the shared clone the worktree was cut from, recorded so
// `status` can answer without re-deriving it from the projects root.
Canonical string `json:"canonical"`
Branch string `json:"branch"`
// Base is the branch the draft pull request targets.
Base string `json:"base"`
// PullRequest is the draft pull request opened from Branch to Base. Zero
// means none was opened; PullRequestError then says why.
PullRequest int `json:"pull_request,omitempty"`
PullRequestURL string `json:"pull_request_url,omitempty"`
PullRequestError string `json:"pull_request_error,omitempty"`
Lease Lease `json:"lease"`
// Links are the live local links this member holds as a consumer. They
// are written by `wb deps propagate local` and are the source of truth
// for `--undo`, for `wb stream status`, and for the refusal that keeps a
// linked worktree from being pushed or landed.
Links []Link `json:"links,omitempty"`
JoinedAt time.Time `json:"joined_at"`
}
Member is one repository inside a stream.
type MemberLink ¶
MemberLink pairs a link with the member that holds it.
type MemberStatus ¶
type MemberStatus struct {
Repository string `json:"repository"`
Role Role `json:"role"`
Worktree string `json:"worktree"`
Branch string `json:"branch"`
Base string `json:"base"`
PullRequest int `json:"pull_request,omitempty"`
PullRequestURL string `json:"pull_request_url,omitempty"`
// PullRequestMissing carries the reason no draft pull request exists.
PullRequestMissing string `json:"pull_request_missing,omitempty"`
// PullRequestRecovery is the exact WB verb that retries the missing
// publication without asking an operator to hand-roll Git or GitHub calls.
PullRequestRecovery string `json:"pull_request_recovery,omitempty"`
// PullRequestBlocked explains when no safe automatic retry exists. A
// divergent shared branch needs an owner decision; advertising join there
// would only repeat the same refusal forever.
PullRequestBlocked string `json:"pull_request_blocked,omitempty"`
// PullRequestUnrecorded says GitHub has the member PR but stream.json does
// not. Status remains read-only and join persists the discovered receipt.
PullRequestUnrecorded bool `json:"pull_request_unrecorded,omitempty"`
// LastPublicationError is persisted evidence from an earlier mutating
// verb. It is separate from current discovery so status output cannot make
// a historical push/create transcript look like work it just performed.
LastPublicationError *PublicationFailure `json:"last_publication_error,omitempty"`
// Unabsorbed is the number of commits on the stream branch that the base
// does not carry by patch identity.
Unabsorbed int `json:"unabsorbed"`
// UnabsorbedClusters names each cluster of patch-identical work, so N
// branches carrying one body of work read as one item rather than N. The
// label is the commit subject; the IDENTITY is the patch id, so two
// commits sharing a message are not collapsed and one change re-applied
// with an edited message still is.
UnabsorbedClusters []string `json:"unabsorbed_clusters,omitempty"`
LeaseHolder string `json:"lease_holder,omitempty"`
RecordedHead string `json:"recorded_head,omitempty"`
LiveLinks int `json:"live_links"`
}
MemberStatus is one member's own row.
type MergedUntagged ¶
type MergedUntagged struct {
Repository string `json:"repository"`
Base string `json:"base"`
LatestTag string `json:"latest_tag,omitempty"`
Commits []string `json:"commits"`
}
MergedUntagged is gap two.
type Phase ¶
type Phase string
Phase is a stream's lifecycle position. It exists because a stream has a state between "does not exist" and "usable": PhaseCreating names the window in which worktrees, branches and draft pull requests are being published.
Recording that window is what makes an interrupted `stream start` recoverable: a half-created stream is a state a verb can see, so `wb stream end` can retire exactly what was published. Without it, a crash after the first push strands branches and pull requests no verb can reach.
const ( // PhaseCreating means the stream record exists and its side effects are // being published. Every member carries its intended coordinates from the // moment the record is written. PhaseCreating Phase = "creating" // PhaseOpen means every member was published successfully. PhaseOpen Phase = "open" // PhaseEnded means `wb stream end` retired it. PhaseEnded Phase = "ended" )
type Preflight ¶
type Preflight struct {
Findings []PreflightFinding `json:"findings"`
}
Preflight is the fleet-readiness report `wb stream start` produces before it creates anything.
Implements: dependency-streams#req:stream-start-proves-the-fleet-is-ready, dependency-streams#req:push-hook-defers-to-ci-on-stream-branches (its concurrency clause).
func RunPreflight ¶
func RunPreflight(ctx context.Context, hub GitHub, checkHooksIn HooksChecker, inputs []PreflightInput) Preflight
RunPreflight runs every readiness check over the proposed members.
Every check reports per repository. A check that cannot run reports PreflightUnknown with its reason rather than passing: this is the same rule `batch-verification-runs-what-ci-runs` applies to skipped mechanisms, and for the same reason — a false assurance is worse than no gate.
func (Preflight) Failed ¶
func (preflight Preflight) Failed() []PreflightFinding
Failed reports the findings that must be resolved or explicitly accepted.
type PreflightFinding ¶
type PreflightFinding struct {
Repository string `json:"repository"`
Check string `json:"check"`
Status PreflightStatus `json:"status"`
Detail string `json:"detail,omitempty"`
}
PreflightFinding is one check's result for one member.
type PreflightInput ¶
type PreflightInput struct {
Repository string
// Path is the checkout the checks read. It is the canonical clone at
// start time: the stream worktrees do not exist yet, and refusing to
// create them is the whole point of running these first.
Path string
// DefaultBranch is the branch the red-`main` check reads.
DefaultBranch string
}
PreflightInput is one member's coordinates for the readiness checks.
type PreflightStatus ¶
type PreflightStatus string
PreflightStatus is one member check's outcome.
const ( // PreflightPass means the check ran and found nothing. PreflightPass PreflightStatus = "pass" // PreflightFail means the check ran and found a problem. A failing check // refuses the start unless the caller explicitly reported past it. PreflightFail PreflightStatus = "fail" // PreflightUnknown means the check could not be established. It is // reported, never silently treated as a pass: an unverified assurance is // worse than no gate. PreflightUnknown PreflightStatus = "unknown" )
type PublicationFailure ¶ added in v0.125.7
type PublicationFailure struct {
Detail string `json:"detail"`
OccurredAt *time.Time `json:"occurred_at,omitempty"`
}
PublicationFailure is one historical failed publication attempt.
type PullRequest ¶
type PullRequest struct {
Number int `json:"number"`
URL string `json:"url"`
Title string `json:"title"`
Body string `json:"body,omitempty"`
Head string `json:"head"`
Base string `json:"base"`
Draft bool `json:"draft"`
State string `json:"state"`
// HeadSHA and MergeSHA are immutable GitHub identities. They are required
// when a stream member was already retired by wb pr land: the missing
// checkout cannot be used as evidence of what the PR actually landed.
HeadSHA string `json:"head_sha,omitempty"`
MergeSHA string `json:"merge_sha,omitempty"`
}
PullRequest is the subset of a pull request stream verbs read.
type Refusal ¶
Refusal is a guard that fired. It carries the stable code a caller branches on and the exact command that satisfies the guard, because `every-refusal-names-the-sanctioned-command` makes a refusal without a next step a hand-written workaround waiting to happen.
type Role ¶
type Role string
Role separates the one repository whose published artifacts the others resolve from the repositories that resolve them. Propagation direction is not symmetric, so the distinction is part of the state rather than something each verb re-derives.
type SquashAbsorptionReceipt ¶ added in v0.127.3
type SquashAbsorptionReceipt struct {
Target string
SourceBranch string
SourceSHA string
CandidateSHA string
LandingSHA string
}
SquashAbsorptionReceipt is the exact immutable receipt a stream passes to the worktree lifecycle after proving a clean member is an ancestor of its merged stream pull request. Cleanup verifies it again before removal.
type StartOptions ¶
type StartOptions struct {
Name string
// Repositories are the members in declaration order. The first is the
// library unless Library names another.
Repositories []string
Library string
// Base overrides the branch the draft pull requests target. Empty
// resolves each repository's own default branch.
Base string
}
StartOptions is one `wb stream start` invocation.
type StartResult ¶
type StartResult struct {
Stream Stream `json:"stream"`
Preflight Preflight `json:"preflight"`
// Reported are the preflight findings that did not refuse the start but
// that the operator must see. `stream-start-proves-the-fleet-is-ready`
// allows a member to be reported instead of refused; nothing may be
// silently passed.
Reported []PreflightFinding `json:"reported,omitempty"`
// TransitiveOmissions names consumers the dependency graph reaches that
// the operator did not include. Leaving one out is legal; leaving one
// out silently is not, because remote propagation bumps only members.
TransitiveOmissions []string `json:"transitive_omissions,omitempty"`
}
StartResult is what `stream start` produced.
type Status ¶
type Status struct {
Stream string `json:"stream"`
Open bool `json:"open"`
// Phase distinguishes a stream still being created from a usable one, so
// an interrupted start is visible rather than looking like a healthy
// stream with missing pull requests.
Phase Phase `json:"phase"`
Library string `json:"library,omitempty"`
Branch string `json:"branch"`
Members []MemberStatus `json:"members"`
// LinkedConsumers is gap one: consumers holding a live local link, and
// the library worktree each is linked to.
LinkedConsumers []LinkedConsumer `json:"linked_consumers"`
// MergedUntagged is gap two: library changes that are merged into the
// base but carry no tag yet.
MergedUntagged *MergedUntagged `json:"merged_untagged,omitempty"`
// ConsumersBehind is gap three: consumers still declaring a version
// older than the library's newest published version.
ConsumersBehind []ConsumerBehind `json:"consumers_behind"`
// OpenAgentPullRequests are the pull requests targeting the stream
// branch. They are the ones GitHub would silently retarget at the base
// if the stream branch were deleted with them still open.
OpenAgentPullRequests []AgentPullRequest `json:"open_agent_pull_requests"`
// Unknowns record every question this report could not answer, so an
// empty gap list never has to be read as "nothing is wrong".
Unknowns []string `json:"unknowns,omitempty"`
}
Status is the whole-stream report.
The three gaps are reported separately and named per repository, because a merged-but-untagged library is not the same problem as a consumer left behind, and collapsing them would hide which one is blocking the stream.
Implements: dependency-streams#req:stream-status-reports-the-three-gaps, dependency-streams#req:stream-backlog-is-counted-by-patch-identity.
type Store ¶
type Store struct {
// Root is <wb-home>/streams. Callers normally build a Store with Open.
Root string
// Now is the clock stream timestamps come from. Tests substitute it.
Now func() time.Time
}
Store reads and writes stream state under WB's home directory.
Every mutation goes through Update, which takes an exclusive lock, re-reads from disk, applies the caller's change and writes atomically. That is what `stream-verbs-re-read-state-before-mutating` requires: a value a verb read when it started is a snapshot, and two verbs running at once must not each write back their own stale copy of the whole stream.
func Open ¶
Open resolves WB's home for one projects root and returns the store below it. It does not create anything; the first Save does.
func OpenAt ¶
OpenAt returns a store rooted at an exact directory. It is the seam tests and embedders use instead of an environment variable.
func (*Store) Archive ¶
Archive renames an ended stream so its name can be used again, keeping the record and its event log intact.
`work-and-event-logs-are-never-pruned` forbids discarding the evidence, but keeping it must not burn the name forever: before this, `start` refused an existing name and `join` refused an ended stream, and the two refusals pointed at each other with no way out.
func (*Store) ArchiveLocked ¶
ArchiveLocked is Archive for a caller already holding the store lock.
func (*Store) Create ¶
Create reserves a stream name and writes its record.
It is the arbiter for two concurrent `stream start` calls on one name: the exclusive create happens under the store-wide lock and BEFORE either call publishes anything, so the loser refuses with no side effects on the remote. The earlier design let both calls create worktrees, push branches and open pull requests and only then arbitrated the file — which left the loser's effects stranded.
The record is written through the same temp-file-and-rename path Update uses, so an interrupted create can never leave a truncated file that would then refuse every later `stream start` on the machine.
func (*Store) CreateLocked ¶
CreateLocked is Create for a caller already holding the store lock through WithStoreLock. Calling it without that lock is a defect, not a deadlock: the lock is not reentrant.
func (*Store) Delete ¶
Delete removes an ended stream and its event log. It refuses an open stream: deleting one would strand its worktrees, branches and pull requests with no record any verb could reach.
func (*Store) Dir ¶
Dir is the per-stream directory. Its layout is part of the contract: the event log sits beside the state file so a stream is one removable unit.
func (*Store) EventLog ¶
func (store *Store) EventLog(name string) *FileEventLog
EventLog returns the log for one stream in this store.
func (*Store) LinkSourcesForWorktree ¶ added in v0.117.2
func (store *Store) LinkSourcesForWorktree(worktree string) ([]StreamLinkSource, error)
LinkSourcesForWorktree returns every live link, across every open stream, whose Library points AT this worktree — the opposite direction from LiveLinksForWorktree. A worktree reported here is not a linked consumer; it is the unpublished source a consumer elsewhere still resolves instead of a published version.
This is the state half of the guard that keeps `wb worktree cleanup` (and any verb that removes a managed worktree while landing, such as `wb pr land` and `wb worktree merge`) from deleting a checkout another stream's consumer still depends on. On 2026-09-07 exactly this happened: a provider worktree was removed while a live stream's `go.work` still named it, and every composed Go command in the consumer failed until the entry was repointed by hand.
func (*Store) List ¶
func (store *Store) List() ([]Stream, []Unreadable, error)
List returns every readable stream in the store, newest first, alongside every stream whose record could not be parsed.
func (*Store) LiveLinksForWorktree ¶
func (store *Store) LiveLinksForWorktree(worktree string) ([]StreamLink, error)
LiveLinksForWorktree returns every live link recorded against one consumer worktree path, across every open stream.
This is the state half of `merge-refuses-a-linked-worktree`. It is exported because the refusal belongs to `wb worktree merge` and `wb pr land`, which must be able to ask the question without importing a stream verb.
func (*Store) Load ¶
Load reads one stream. It returns ErrNotFound when the stream does not exist, so a caller can tell "no such stream" from "the state is unreadable" without matching on prose.
func (*Store) RepositoryStream ¶
RepositoryStream answers the one-open-stream-per-repository question: which open stream, if any, already holds this repository. It is what `stream start` refuses on, and what names the holder in that refusal.
func (*Store) Update ¶
Update applies mutate to the freshly re-read stream under an exclusive lock and writes the result atomically. mutate may be called only once; returning an error leaves the stored stream untouched.
func (*Store) WithStoreLock ¶
WithStoreLock runs body while holding the store-wide lock, so a caller can make "no repository is in another open stream" and "reserve this name" one indivisible decision. Without it the guard is a check with a gap after it.
type Stream ¶
type Stream struct {
SchemaVersion int `json:"schema_version"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Phase is the lifecycle position. An empty value reads as PhaseOpen so a
// record written by an older binary stays usable.
Phase Phase `json:"phase,omitempty"`
// ArchivedFrom is set when a stream was archived to free its name for a
// new stream. It records the name the record used to carry.
ArchivedFrom string `json:"archived_from,omitempty"`
// EndedAt is set by `wb stream end`. An ended stream is kept, not
// deleted: its event log and link history are the evidence a later
// report is built from, and `work-and-event-logs-are-never-pruned`
// forbids discarding them as a side effect of ending the work.
EndedAt *time.Time `json:"ended_at,omitempty"`
Members []Member `json:"members"`
// LinkedConsumers are explicitly admitted alternate managed worktrees of a
// repository already represented by Members. They participate only in
// local dependency linking; stream branch, PR and repository ownership stay
// with the member. Recording the exact checkout keeps undo and landing
// guards authoritative without pretending the alternate branch is a member.
LinkedConsumers []LinkedConsumerBinding `json:"linked_consumers,omitempty"`
}
Stream is the durable record of one named cross-repository unit of work.
func (Stream) Consumers ¶
Consumers returns every member that is not the library, in recorded order.
func (Stream) Lifecycle ¶
Lifecycle resolves the phase, treating an empty value as PhaseOpen so a record written before phases existed keeps its meaning.
func (Stream) LinkedConsumer ¶ added in v0.117.3
func (stream Stream) LinkedConsumer(repository string) (LinkedConsumerBinding, bool)
LinkedConsumer finds one admitted alternate managed worktree by owner/repository. It is distinct from Member: a linked consumer holds only local links, never the stream branch, PR, or repository ownership.
func (Stream) LiveLinks ¶
func (stream Stream) LiveLinks() []MemberLink
LiveLinks returns every live link the stream currently records, paired with the consumer repository holding it.
type StreamLink ¶
type StreamLink struct {
Stream string `json:"stream"`
Repository string `json:"repository"`
Link Link `json:"link"`
}
StreamLink is one live link qualified by the stream and repository holding it, so a refusal can name both without a second lookup.
type StreamLinkSource ¶ added in v0.117.2
type StreamLinkSource struct {
Stream string `json:"stream"`
ConsumerRepository string `json:"consumer_repository"`
// ConsumerWorktree is the linked consumer's worktree path, so a refusal
// can name the exact repoint command without a second lookup.
ConsumerWorktree string `json:"consumer_worktree"`
Link Link `json:"link"`
}
StreamLinkSource is one live link whose unpublished source is a worktree under consideration for removal, qualified by the stream and consumer holding it, so a refusal can name both without a second lookup.
type Unreadable ¶
type Unreadable struct {
Name string `json:"name"`
Path string `json:"path"`
Reason string `json:"reason"`
}
Unreadable names one stream whose record could not be parsed.
It is reported rather than returned as an error for the whole store: a single truncated file must not refuse every `wb stream start` on the machine. It is also never silently dropped — a stream WB cannot read may hold live links, and pretending it is absent is the failure mode `status` exists to prevent.
type WorktreeCreator ¶
type WorktreeCreator interface {
// PlannedWorktree is where Create will publish one repository's checkout.
// It performs no side effect, so the stream record can carry each
// member's intended coordinates BEFORE anything is created — which is
// what makes an interrupted start recoverable.
PlannedWorktree(task, repository string) (string, error)
Create(ctx context.Context, task, branch string, repositories []string) ([]CreatedWorktree, error)
// Remove retires one member's worktree through the existing cleanup
// path. `stream end` delegates removal rather than deleting directories.
Remove(ctx context.Context, task, repository, worktree string, receipt *SquashAbsorptionReceipt) error
}
WorktreeCreator is the existing worktree creation path.
`stream-is-a-named-set-of-worktrees` forbids inventing a second creation path, so this port exists to delegate to `wb worktree create` — with its branch policy, prompt archival and fleet-wide claim — rather than to reimplement any of it.