Help us shape the future of Go! Take the 2026 Go Developer Survey

engine

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 45 Imported by: 0

Documentation

Overview

Package engine implements every Ghostbranch operation. It is the only package that orchestrates git, the forge and the leak scanner, and the only one allowed to push to the public repo: from promote and apply, after a green gate on the exact tree being published.

Index

Constants

View Source
const (
	AfterArchived   = "archived"
	AfterKept       = "kept"
	AfterIncomplete = "incomplete"
)

What happened to a published feature's shadow branch (data.after_publish).

View Source
const (
	EventWaiting = "waiting"
	EventCheck   = "check"
	EventSettled = "settled"
)

Job event types, one per line of wait --json (schemas/v0/wait-event.schema.json).

View Source
const (
	DoctorOK      = "ok"
	DoctorWarn    = "warn"
	DoctorFail    = "fail"
	DoctorSkipped = "skipped"
)

Doctor check statuses.

View Source
const (
	MinGitMajor = 2
	MinGitMinor = 38
)

The oldest git Ghostbranch supports: drift and the gate use git merge-tree --write-tree, new in 2.38. (The guard alone needs 2.31.)

View Source
const (
	DefaultPollInterval = 15 * time.Second
	DefaultSettleWindow = 60 * time.Second
	// DefaultCIAppearWindow is how long sync looks for the workflow runs
	// its pushes start in the shadow repo.
	DefaultCIAppearWindow = 30 * time.Second
	// DefaultCIAppearPoll is how often sync looks within that window.
	DefaultCIAppearPoll = 3 * time.Second
	// DefaultCancelVerifyWindow is how long sync waits for the runs it
	// cancelled to finish before it force-cancels the ones still going.
	DefaultCancelVerifyWindow = 30 * time.Second
	// DefaultForceCancelWindow is how long it then waits for the
	// force-cancelled runs to finish.
	DefaultForceCancelWindow = 15 * time.Second
	// OverlayBranch holds the overlay and is the shadow repo's default branch.
	OverlayBranch = "ghostbranch"
)
View Source
const AgentOnLoginMessage = "" /* 323-byte string literal not displayed */

AgentOnLoginMessage is the warning when a likely agent uses the person's stored login: doctor's token check says it, and the CLI gives it as the AGENT_ON_PERSON_LOGIN warning.

View Source
const DefaultLogLimit = 50

DefaultLogLimit is how many entries log shows unless told otherwise.

View Source
const DefaultSyncSchedule = "23 */6 * * *"

DefaultSyncSchedule is init --sync's cron: every 6 hours, at minute 23.

Variables

View Source
var DoctorCheckIDs = []string{"tools", "token", "pair", "guard", "state", "ci"}

DoctorCheckIDs are doctor's checks, in the order it runs and reports them.

Functions

func ClosableWithoutConsole

func ClosableWithoutConsole(r FeatureReport) bool

ClosableWithoutConsole is the one rule for unattended deletion: published, merged and unmoved, or merged with its branch already gone, when the gate (if any) is for what was published (nothing left on it to lose). A record this binary can't read as a whole never is.

func LoadCurate

func LoadCurate(path string) ([]plan.Group, error)

LoadCurate reads a --curate file and checks its shape: at least one group, each naming one or more commits by SHA (7 to 40 lowercase hex characters) with a non-empty message. Whether the SHAs are the feature's commits is ResolveGroups' job.

func Location

func Location(f leak.Finding) string

Location is where a finding is: path:line, the path, or the text field.

func ParseSince

func ParseSince(s string, now time.Time) (time.Time, error)

ParseSince reads log's --since: a positive duration back from now, such as 24h, or an RFC 3339 time. "" is no limit.

func RebaseSteps

func RebaseSteps(remote, branch, onto, upstream string) [][]string

RebaseSteps are the git commands that rebase branch's own commits onto onto (a feature, or "main") in a checkout whose remote for the shadow repo is remote, then push the result leased. upstream is where branch's own commits start: a fork-point SHA, "main" for the shadow repo's main, or "" for a plain rebase. Ghostbranch never runs them: a person or their agent does, and resolves any conflict.

func ResolveGroups

func ResolveGroups(groups []plan.Group, sources []string) ([]plan.Group, error)

ResolveGroups maps each SHA in groups to the one feature commit it names, and checks the groups name every commit exactly once.

func ValidatePromoteOptions

func ValidatePromoteOptions(opt *PromoteOptions) error

ValidatePromoteOptions checks everything about a promote that needs no network, so a bad flag fails before any CI runs. An empty Mode means promote.default_mode, which is checked once the config is read.

func ValidateWaiversAdd

func ValidateWaiversAdd(opt WaiversAddOptions) ([]string, string, error)

ValidateWaiversAdd checks everything about waivers add that needs no network, so bad input fails before any token or console is needed.

Types

type AgentsAllowOptions

type AgentsAllowOptions struct {
	For          string // 1h, 6h (default), 1d, 7d, 15d, 30d or 90d
	UntilRevoked bool
}

type AgentsAllowResult

type AgentsAllowResult struct {
	Grant    grant.Grant      `json:"grant"`
	Replaced *grant.Grant     `json:"replaced"`
	Preview  *preview.Preview `json:"preview"`
}

type AgentsRevokeResult

type AgentsRevokeResult struct {
	Revoked []grant.Grant    `json:"revoked"`
	Preview *preview.Preview `json:"preview"`
}

type AgentsStatusResult

type AgentsStatusResult struct {
	Pair            string        `json:"pair"`
	Grants          []GrantStatus `json:"grants"`
	TokenCanPublish string        `json:"token_can_publish"`
	TokenKind       string        `json:"token_kind"`
	Token           TokenCheck    `json:"token"`
}

type ApplyOptions

type ApplyOptions struct {
	Plan string // plan ID or path to a plan .json
	Yes  bool
	// Archive hands the published features off after the publish (6.5):
	// the person picks Archive or Keep at the console, or, without one,
	// Archive applies. false keeps them without asking (R3).
	Archive bool
	// Lookup is the pair to look in when the plan file isn't on this
	// machine (--repo or the checkout's; nil when none resolves). A plan
	// that is here names its own pair.
	Lookup *workspace.Pair
	// LookupErr is why a --repo that was passed named no pair here. A plan
	// this machine lacks then fails with it (PAIR_NOT_FOUND, say), so the
	// ledger and the caller see the same code (re-review N4).
	LookupErr error
}

type ApplyResult

type ApplyResult struct {
	PlanID       string           `json:"plan_id"`
	PublicBranch string           `json:"public_branch"`
	PublicPR     int              `json:"public_pr"`
	PRURL        string           `json:"pr_url"`
	Resumed      bool             `json:"resumed"`
	Approval     *ledger.Approval `json:"approval"`
	Waived       []WaivedFinding  `json:"waived"`
	Preview      *preview.Preview `json:"preview"`
	// PRs is every PR the plan opened, bottom first (one for a plan of one
	// feature); StackNumber is the native GitHub stack, null when none.
	// PublicBranch, PublicPR and PRURL are the top PR's.
	PRs         []plan.StackPR `json:"prs"`
	StackNumber *int           `json:"stack_number"`
	// KeepsHistory: some PR publishes more than one commit in preserve or
	// curated mode, which "Squash and merge" would flatten (F-31).
	// PublicRepo is the plan's public repo. Both are for next.
	KeepsHistory bool   `json:"-"`
	PublicRepo   string `json:"-"`
	// AfterPublish is what happened to each published feature's shadow
	// branch: archived, kept, or incomplete (ARCHIVE_INCOMPLETE). Never
	// null. Archived lists the features archived, bottom first.
	AfterPublish map[string]string `json:"after_publish"`
	Archived     []string          `json:"-"`
	// Landed: the plan's updates had already landed, so this run only
	// recorded them and archived nothing (finishLanded, R14), though the
	// run that died may have been asked to. For next.
	Landed bool `json:"-"`
	// contains filtered or unexported fields
}

type AuditMinutes

type AuditMinutes struct {
	Billed        bool    `json:"billed"`
	ScheduledSync *string `json:"scheduled_sync"`
}

AuditMinutes is whether the shadow repo's Actions minutes are billed.

type AuditResult

type AuditResult struct {
	Public       string           `json:"public"`
	Shadow       string           `json:"shadow"`
	Guards       []workflow.Guard `json:"guards"`
	GuardMode    string           `json:"guard_mode"` // ci.rewrite_repo_guards
	Secrets      AuditSecrets     `json:"secrets"`
	PushTriggers []string         `json:"push_triggers"`
	Minutes      AuditMinutes     `json:"minutes"`
	Overlay      []ManagedFile    `json:"overlay"`
}

AuditResult is the audit command's data.

type AuditSecrets

type AuditSecrets struct {
	Referenced   []SecretRef `json:"referenced"`
	Listed       bool        `json:"listed"`
	Missing      []string    `json:"missing"`
	Inherit      []string    `json:"inherit"`      // "<path>:<job>"
	Environments []string    `json:"environments"` // "<path>:<job>"
}

AuditSecrets is what the audit knows about the shadow repo's secrets.

type CancelledRun

type CancelledRun struct {
	ID       int64  `json:"id"`
	Workflow string `json:"workflow"`
	URL      string `json:"url"`
}

CancelledRun is a workflow run sync cancelled in the shadow repo.

type ClosedFeature

type ClosedFeature struct {
	Name     string            `json:"name"`
	PublicPR int               `json:"public_pr"`
	Deleted  map[string]string `json:"deleted"`
	// Reparented are the children moved onto main in the push that closed
	// it; never null.
	Reparented []string `json:"reparented"`
	// GatePRLeftOpen is the gate PR the close couldn't close, 0 when none;
	// for human output only.
	GatePRLeftOpen int `json:"-"`
}

ClosedFeature is a merged feature sync closed: its public PR and every shadow ref deleted, with the SHA it pointed at.

type CommitInfo

type CommitInfo struct {
	Source  string   `json:"source"`
	Subject string   `json:"subject"`
	Author  string   `json:"author"` // "Name <email>" as committed
	Files   []string `json:"files"`  // paths it changes, overlay paths left out
}

CommitInfo is one feature commit as the gate replayed it, for an agent drafting a --curate file.

type DoctorCheck

type DoctorCheck struct {
	ID      string         `json:"id"`
	Status  string         `json:"status"`
	Message string         `json:"message"`
	Fix     string         `json:"fix"`
	FixArgv []string       `json:"-"`
	Details map[string]any `json:"details"`
	// Unregistered is the public repo a failing pair check is about when
	// it isn't set up here: next offers both init forms (F-27).
	Unregistered string `json:"-"`
	// PersonMessage and PersonFixArgv replace Message and the fix in the
	// output for people, when set: a person runs plain init where an agent
	// gets the --plan forms. The JSON keeps Message and Fix as they are.
	PersonMessage string   `json:"-"`
	PersonFixArgv []string `json:"-"`
}

DoctorCheck is one check's finding. Fix is a command that fixes it, or "" when there is none to run; FixArgv is that command as argv (nil with no fix), for next. Fix is FixArgv joined with spaces, for reading only.

type DoctorOptions

type DoctorOptions struct {
	// Repo is --repo. Empty means the current checkout's pair; never the
	// workspace's only pair, so outside a checkout the pair checks skip.
	Repo    string
	Remotes []workspace.Remote // the current checkout's remotes
	Dir     string             // the current directory; "" when unknown
	// GitVersion is `git version`'s output, or GitErr why it didn't run.
	GitVersion string
	GitErr     error
	// Version and Commit are this binary's build.
	Version, Commit string
	// TokenErr is why no token resolved; the stored login is never
	// refreshed for doctor, since it writes nothing.
	TokenErr error
	// Login is the stored login when the token came from it, else nil.
	Login *auth.Login
	// WorkspaceErr is why the workspace couldn't be opened.
	WorkspaceErr error
	Getenv       func(string) string
	// ForPerson is a run whose output a person reads (not --json): only
	// then does doctor work out the plain init line for a repo that isn't
	// set up here (PersonMessage).
	ForPerson bool
}

DoctorOptions is what doctor needs from outside the engine; the CLI fills it. Doctor also reads e.Token and e.TokenSource ("" when none resolved), e.Forge (nil then), e.LooksLikeAgent, and e.WS (nil when the workspace couldn't be opened).

type DoctorResult

type DoctorResult struct {
	Checks []DoctorCheck `json:"checks"`
	// UpdateCheck is the update checker's state from a fresh look (6.4a).
	// The CLI sets it; the engine never looks.
	UpdateCheck *updatecheck.Status `json:"update_check,omitempty"`
}

DoctorResult is doctor's data: every check, in DoctorCheckIDs order.

func (*DoctorResult) Failed

func (r *DoctorResult) Failed() []string

Failed is the IDs of the failing checks, never nil.

func (*DoctorResult) Summary

func (r *DoctorResult) Summary() DoctorSummary

Summary counts r's checks by status.

type DoctorSummary

type DoctorSummary struct {
	OK      int      `json:"ok"`
	Warn    int      `json:"warn"`
	Fail    int      `json:"fail"`
	Skipped int      `json:"skipped"`
	Failed  []string `json:"failed"`
}

DoctorSummary counts a result's checks by status; guided init reports it.

type Drift

type Drift struct {
	Status string   `json:"status"` // clean | conflicts | unknown | n/a (branch gone)
	Files  []string `json:"files"`
}

Drift is whether a feature still merges cleanly onto public main.

type Engine

type Engine struct {
	Forge forge.Forge
	WS    *workspace.Workspace
	Actor string
	Token string
	// TokenSource names where Token came from, for error messages.
	TokenSource string
	// LooksLikeAgent is auth.LooksLikeAgent for this run: it is likely an
	// agent or a script, not a person at a terminal. Doctor and agents
	// status read it to spot an agent on a person's stored login.
	LooksLikeAgent bool
	Now            func() time.Time
	// Sleep waits d, or returns ctx.Err() as soon as ctx is done.
	Sleep func(context.Context, time.Duration) error
	Rand  io.Reader
	// Progress receives the steps and notes of slow work; nil prints nothing.
	Progress     progress.Progress
	Approver     approval.Approver
	Grants       *grant.Store
	Waivers      *waiver.Store
	Editor       func(initial string) (string, error)
	NewScanner   func(leak.Rules) (leak.Scanner, error)
	PollInterval time.Duration
	SettleWindow time.Duration
	// CIAppearWindow is how long sync looks for the workflow runs its
	// pushes start; zero looks once. CIAppearPoll is how often it looks
	// (zero means DefaultCIAppearPoll).
	CIAppearWindow time.Duration
	CIAppearPoll   time.Duration
	DryRun         bool
	// OnPush observes every push before it happens (tests).
	OnPush func(op, remote string, refspecs []string)
	// contains filtered or unexported fields
}

Engine runs operations for one workspace. Editor is nil when there is no interactive terminal. Approver is nil when no person can be asked at a console; Grants is nil when grants are not available (tests).

func (*Engine) AgentsAllow

func (e *Engine) AgentsAllow(ctx context.Context, p workspace.Pair, opt AgentsAllowOptions) (res *AgentsAllowResult, warns []contract.Warning, err error)

AgentsAllow lets agents publish to p from this machine without the console code. Making the grant itself needs a person at a console, so an agent cannot grant itself (spec §7.2).

func (*Engine) AgentsRevoke

func (e *Engine) AgentsRevoke(ctx context.Context, p *workspace.Pair, all bool) (res *AgentsRevokeResult, warns []contract.Warning, err error)

AgentsRevoke removes the grant for p, or every grant with all. Taking power away needs no console.

func (*Engine) AgentsStatus

func (e *Engine) AgentsStatus(ctx context.Context, p *workspace.Pair) (*AgentsStatusResult, []contract.Warning, error)

AgentsStatus lists grants and, for a pair, whether the current token can push to its public repo (the token-split check, spec §8).

func (*Engine) Apply

func (e *Engine) Apply(ctx context.Context, opt ApplyOptions) (res *ApplyResult, warns []contract.Warning, err error)

Apply publishes a saved plan if the repos still match it. Every step is safe to re-run, so an interrupted apply finishes on the next run.

func (*Engine) Audit

Audit reports what keeps the shadow repo's CI from matching public CI. It changes nothing and is not ledgered.

func (*Engine) Doctor

func (e *Engine) Doctor(ctx context.Context, opt DoctorOptions) (*DoctorResult, error)

Doctor checks this machine's setup and, when there is one, the pair. It changes nothing in the repos, the records or the keychain: no record, ledger entry, lock, keychain entry, chmod, push or forge write. It fetches into the local mirror (making it if needed), as status does; the CLI also refreshes the update check's cache. Every check reports, whatever the others found; it stops early only on Ctrl-C, with the checks so far and INTERRUPTED.

func (*Engine) FeatureAbandon

func (e *Engine) FeatureAbandon(ctx context.Context, p workspace.Pair, name string, undo bool) (res *FeatureAbandonResult, warns []contract.Warning, err error)

FeatureAbandon parks a feature (or, with undo, restores it) by flipping Abandoned in its metadata, a leased push to the shadow repo. Its branch, gate and plans stay; nothing is deleted, so no console is asked.

func (*Engine) FeatureArchive

func (e *Engine) FeatureArchive(ctx context.Context, p workspace.Pair, name string) (res *FeatureArchiveResult, warns []contract.Warning, err error)

FeatureArchive hands a published feature off to the public repo (spec § 4.3.5): only when its PR is open and nothing is built on it (R8, R13). It deletes nothing (the archive keeps the head), so it needs no console. An archived feature is exit 0 with changed false.

func (*Engine) FeatureClose

func (e *Engine) FeatureClose(ctx context.Context, p workspace.Pair, name string) (res *FeatureCloseResult, warns []contract.Warning, err error)

FeatureClose deletes a feature from the shadow repo: its branch, gate branch, metadata and plan refs, in one atomic push leased on what the survey saw, then closes its gate draft PR and cleans up this machine. Only a published, merged and unmoved feature (or a merged one whose branch is gone) closes without a person at a console; --plan asks no one and changes nothing. A feature whose record can't be read (corrupt, or from a newer Ghostbranch) closes too, but only at a console.

func (*Engine) FeatureFold

func (e *Engine) FeatureFold(ctx context.Context, p workspace.Pair, opt FoldOptions) (res *FoldResult, warns []contract.Warning, err error)

FeatureFold folds a stacked feature into its parent: the parent's branch fast-forwards to the feature's head and the feature closes, each of its children stacked on the parent instead, all in one atomic push leased on what fold read. Every commit of the feature ends up on the parent, so no console is asked. A parent whose tip the feature doesn't contain is FOLD_NEEDS_REBASE with the git steps: Ghostbranch never rebases, merges or resolves a conflict here.

func (*Engine) FeatureList

func (e *Engine) FeatureList(ctx context.Context, p workspace.Pair, all bool) (*FeatureListResult, []contract.Warning, error)

FeatureList surveys the pair's features, leaving out abandoned ones unless all. It only reads.

func (*Engine) FeaturePull

func (e *Engine) FeaturePull(ctx context.Context, p workspace.Pair, opt PullOptions) (res *PullResult, warns []contract.Warning, err error)

FeaturePull reads the feature's open PR and the commits on its branch Ghostbranch didn't publish (prHead, through comparePublished: patch-id and message), brings them into the shadow branch (pullHead), and records them, in one atomic push leased on the branch head and the record it read. With nothing foreign it changes nothing. It touches only the shadow repo.

func (*Engine) FeatureRestack

func (e *Engine) FeatureRestack(ctx context.Context, p workspace.Pair, opt RestackOptions) (res *RestackResult, warns []contract.Warning, err error)

FeatureRestack replays, in an isolated worktree, each feature above opt.Feature onto its parent's tip, bottom first, or with opt.Onto moves opt.Feature itself onto main or another feature first. Each branch and its record go in one atomic push leased on what the restack read, so a branch someone pushed to meanwhile is left alone. The first conflict stops it with the git steps; branches already restacked stay restacked. Ghostbranch never resolves a conflict. It touches only the shadow repo.

func (*Engine) FeatureShow

func (e *Engine) FeatureShow(ctx context.Context, p workspace.Pair, name string) (*FeatureShowResult, []contract.Warning, error)

FeatureShow reports one feature in full: its survey report, its own commits (since its fork point, or since public main) and its ledger entries. It only reads. A feature whose record can't be read returns the read error.

func (*Engine) FeatureStart

func (e *Engine) FeatureStart(ctx context.Context, p workspace.Pair, name string, opts ...FeatureStartOptions) (res *FeatureStartResult, warns []contract.Warning, err error)

FeatureStart creates a feature branch and its metadata ref in one atomic push: from shadow main, or, with From, stacked on another feature's tip. It does not touch the user's checkout, and never writes the parent's record.

func (*Engine) Gate

func (e *Engine) Gate(ctx context.Context, p workspace.Pair, name string) (*GateResult, []contract.Warning, error)

Gate rebases a feature onto public main, scans what would be published, runs CI on the candidate through a draft shadow PR, and records the result. The pair lock is free while CI runs. It never touches the public repo.

func (*Engine) GateDetached

func (e *Engine) GateDetached(ctx context.Context, p workspace.Pair, name string) (*GateResult, []contract.Warning, error)

GateDetached is gate --detach: it records the gate as a pending job and returns without waiting on CI; wait finishes the job.

func (*Engine) Init

func (e *Engine) Init(ctx context.Context, opt InitOptions) (res *InitResult, warns []contract.Warning, err error)

Init creates (or adopts) the shadow repo, mirrors public branches and tags into it, creates the overlay branch as its default branch, and registers the pair. Every step checks before acting, so re-running resumes a partial init.

func (*Engine) LedgerWarnings

func (e *Engine) LedgerWarnings() []contract.Warning

LedgerWarnings returns the LEDGER_NOT_WRITTEN warnings since the last call, and forgets them.

func (*Engine) Promote

func (e *Engine) Promote(ctx context.Context, p workspace.Pair, opt PromoteOptions) (res *PromoteResult, warns []contract.Warning, err error)

Promote gates the feature (reusing a fresh green gate), builds the chain of public commits its history mode publishes, scans everything that would go public, saves a plan, and applies it once approved.

func (*Engine) Remove

func (e *Engine) Remove(ctx context.Context, p workspace.Pair) (res *RemoveResult, warns []contract.Warning, err error)

Remove unregisters p from this machine: it revokes the pair's agent grants and waivers, removes its plan files and its directory, and drops it from workspace.json. It touches nothing remote, and the ledger keeps its history. It drops grants and waivers, so it needs a person at a console; --plan does not.

func (*Engine) Status

Status reports the pair's health (shadow main behind, the managed overlay files, scheduled sync) and surveys every feature. Like the other pair commands it warns SHADOW_BEHIND and OVERLAY_OUTDATED. It only reads: no push and no ledger entry.

func (*Engine) Sync

func (e *Engine) Sync(ctx context.Context, p workspace.Pair, o SyncOptions) (res *SyncResult, warns []contract.Warning, err error)

Sync fast-forwards each branch in sync.branches in the shadow repo to the public branch, creating it when missing. It never force-pushes: a diverged branch is reported and left alone while the others still fast-forward. The top-level main fields describe main. A push with the person's token starts the shadow repo's push-triggered workflows, re-testing what public CI already tested, so sync then cancels those runs unless o.NoCancel. Last, unless o.NoClose, it surveys the features: it records GitHub's restack of the public branches of a stack above a merged PR (the same changes on a new base) and flags any other change to them (PUBLIC_BRANCH_CHANGED), then closes the features whose public PR merged and whose branch hasn't moved, bottom of a stack first, moving each closing feature's children onto main in the push that closes it (see closeMerged). --no-close skips all of that, the survey too. A branch whose push fails is reported (SYNC_PUSH_FAILED) and the rest still go; the first failure is the sync's error, after the cancels and closes. A dry run reports each branch's target in To.

func (*Engine) Upgrade

func (e *Engine) Upgrade(ctx context.Context, p workspace.Pair) (res *UpgradeResult, warns []contract.Warning, err error)

Upgrade brings the pair's managed overlay files up to this binary's templates and the overlay config: it adds, updates or removes them in one overlay commit, leased on the overlay branch. It writes only overlay paths in the shadow repo.

func (*Engine) Wait

func (e *Engine) Wait(ctx context.Context, p workspace.Pair, job string, opt WaitOptions) (res *GateResult, warns []contract.Warning, err error)

Wait finishes a gate job: it waits on CI for the pending gate the job names and records the outcome over it. A job already finished returns its recorded result at once, writing nothing. It never touches the public repo.

func (*Engine) WaiversAdd

func (e *Engine) WaiversAdd(ctx context.Context, p workspace.Pair, opt WaiversAddOptions) (res *WaiversAddResult, warns []contract.Warning, err error)

WaiversAdd waives findings in what the feature would publish now (spec §7.2). It never trusts recorded results: it rebases, strips and scans again, with no push and no CI, and shows the person what it found. Only a person at a console can confirm; --yes and grants never apply.

func (*Engine) WaiversList

func (e *Engine) WaiversList(ctx context.Context, p *workspace.Pair) (*WaiversListResult, []contract.Warning, error)

WaiversList lists stored waivers, for one pair or all. It needs no network.

func (*Engine) WaiversRevoke

func (e *Engine) WaiversRevoke(ctx context.Context, p *workspace.Pair, ids []string, all bool) (res *WaiversRevokeResult, warns []contract.Warning, err error)

WaiversRevoke removes waivers by id for one pair, or every waiver with all. Taking a waiver away only makes the gate stricter, so it needs no console. An id with no waiver for the repo is WAIVER_UNKNOWN, and nothing is revoked. With all it also clears a waivers.json that does not decode.

type FeatureAbandonResult

type FeatureAbandonResult struct {
	Feature   string           `json:"feature"`
	Abandoned bool             `json:"abandoned"` // the new value
	Changed   bool             `json:"changed"`   // false when it already had that value
	Preview   *preview.Preview `json:"preview"`
}

FeatureAbandonResult is a feature parked or restored.

type FeatureArchiveResult

type FeatureArchiveResult struct {
	Feature string `json:"feature"`
	// Changed is false when the feature was already archived.
	Changed bool `json:"changed"`
	// Archived is the record's archive; null under --plan before it exists.
	Archived *feature.Archived `json:"archived"`
	// Moved is every shadow ref the archive created or deleted, with its
	// SHA; never null.
	Moved  map[string]string `json:"moved"`
	GatePR *int              `json:"gate_pr"` // the gate draft PR closed; null when none was open
	// PublicPR and Branch are the PR and its public branch: push fixes there.
	PublicPR int              `json:"public_pr"`
	Branch   string           `json:"branch"`
	Preview  *preview.Preview `json:"preview"`
	// PRState is the public PR's state when the feature was already
	// archived (open, merged, closed or unknown), for next: a merged or
	// closed PR takes no more pushes. "" otherwise.
	PRState string `json:"-"`
}

FeatureArchiveResult is a feature handed off by feature archive.

type FeatureCloseResult

type FeatureCloseResult struct {
	Feature      string            `json:"feature"`
	State        feature.State     `json:"state"`         // the state it was closed from
	Deleted      map[string]string `json:"deleted"`       // shadow ref -> SHA it pointed at; never null
	GatePR       *int              `json:"gate_pr"`       // the draft PR closed (by us, or by GitHub as its branch went), nil when none was open
	NeedsConsole bool              `json:"needs_console"` // closing takes a person at a console (not ClosableWithoutConsole); set under --plan too
	Approval     *ledger.Approval  `json:"approval"`      // console; nil under --plan, and when none was needed
	// Reparented are the children moved onto main in the push that closed
	// this feature; never null.
	Reparented []string         `json:"reparented"`
	Preview    *preview.Preview `json:"preview"`
	// GatePRLeftOpen is the gate PR the close couldn't close, 0 when none;
	// for human output only.
	GatePRLeftOpen int `json:"-"`
}

FeatureCloseResult is what closing a feature deleted.

type FeatureListResult

type FeatureListResult struct {
	Features []FeatureReport `json:"features"` // never null; abandoned only with all
}

FeatureListResult is the features table.

type FeatureReport

type FeatureReport struct {
	Name   string        `json:"name"`
	State  feature.State `json:"state"`
	Branch string        `json:"branch"`
	Head   string        `json:"head"` // "" when the branch is gone
	Base   string        `json:"base"`
	// Parent is the feature it is stacked on, or "main"; "" when the record
	// couldn't be read. Children are the live features stacked directly on
	// it, by name, never null. Stack is the native GitHub stack its PR is in.
	Parent    string       `json:"parent"`
	Children  []string     `json:"children"`
	Stack     *int         `json:"stack"`
	Drift     Drift        `json:"drift"`
	Gate      *GateSummary `json:"gate"`      // nil when never gated
	PublicPR  *int         `json:"public_pr"` // nil when never published
	PRState   string       `json:"pr_state"`  // "", open, merged, closed, unknown
	PlanID    string       `json:"plan_id"`
	Moved     bool         `json:"moved"` // since publish; false when unpublished; for an archived feature, its archive head (and any archive ref) against the published head; otherwise meaningless when the branch is gone (branch_gone reads only PRState)
	Abandoned bool         `json:"abandoned"`
	// PR is how its open public PR compares with the feature; nil unless
	// it is published, the PR is open and the feature's branch exists.
	PR *PRSync `json:"pr,omitempty"`
	// Archived is set once the feature was handed off (6.5): its archive
	// ref and head.
	Archived *feature.Archived `json:"archived,omitempty"`
	// PublicBranch is the public branch it was published to, for next;
	// "" when unpublished.
	PublicBranch string `json:"-"`
	// PartlyArchived, for next: an archive ref exists but the record
	// isn't marked, so an archive stopped partway (feature archive
	// finishes it).
	PartlyArchived bool `json:"-"`
	// Error is why the feature couldn't be surveyed; set only with state
	// unreadable. feature show returns it as its error.
	Error string `json:"error,omitempty"`
	// contains filtered or unexported fields
}

FeatureReport is one feature as the survey sees it.

type FeatureShowResult

type FeatureShowResult struct {
	Feature FeatureReport  `json:"feature"`
	Commits []CommitInfo   `json:"commits"` // the feature's own commits (since its fork point, else since public main), oldest first; [] when the branch is gone
	Ledger  []ledger.Entry `json:"ledger"`  // this pair's entries for this feature, oldest first
}

FeatureShowResult is one feature in full.

type FeatureStartOptions

type FeatureStartOptions struct {
	// From is the feature to stack the new one on: it starts at that
	// feature's tip in the shadow repo and records it as its parent, with
	// the tip as forked_at. "" or "main" starts from shadow main.
	From string
}

FeatureStartOptions are feature start's options beyond the feature's name.

type FeatureStartResult

type FeatureStartResult struct {
	Name   string `json:"name"`
	Branch string `json:"branch"`
	Base   string `json:"base"`
	// Parent is the feature this one is stacked on, or "main".
	Parent       string           `json:"parent"`
	Checkout     [][]string       `json:"checkout"`      // argvs that get the branch into a checkout, in order
	CheckoutHint string           `json:"checkout_hint"` // Checkout as one shell line
	Preview      *preview.Preview `json:"preview"`
	// contains filtered or unexported fields
}

func (*FeatureStartResult) GateFirst

func (r *FeatureStartResult) GateFirst() string

GateFirst is the feature below the new one, in its stack, that next gates first; "" when every one below has a fresh green gate.

func (*FeatureStartResult) SetCheckout

func (r *FeatureStartResult) SetCheckout(argvs [][]string)

SetCheckout sets Checkout and CheckoutHint, its one-line form.

type FoldOptions

type FoldOptions struct {
	Feature string // the stacked feature to fold into its parent
}

FoldOptions are feature fold's inputs.

type FoldResult

type FoldResult struct {
	Feature    string            `json:"feature"`    // the folded feature, now closed
	Parent     string            `json:"parent"`     // the feature it was folded into
	From       string            `json:"from"`       // the parent's head before
	To         string            `json:"to"`         // the parent's head now: the folded feature's head
	Reparented []string          `json:"reparented"` // the folded feature's children, now stacked on Parent; never null
	Deleted    map[string]string `json:"deleted"`    // the folded feature's refs deleted, with their SHAs; never null
	GatePR     *int              `json:"gate_pr"`    // its gate draft PR, closed; nil when none was open
	Preview    *preview.Preview  `json:"preview"`
}

FoldResult is a stacked feature folded into its parent.

type ForeignCommit

type ForeignCommit struct{ SHA, Author, Subject string }

ForeignCommit is a commit on a published feature's public branch that Ghostbranch didn't publish (and that isn't GitHub's own restack).

type GateResult

type GateResult struct {
	Feature     string `json:"feature"`
	FeatureHead string `json:"feature_head"`
	PublicMain  string `json:"public_main"`
	// Parent is the feature the gate stacked on; "" when it built on
	// public main.
	Parent string `json:"parent"`
	// Base is the commit the feature's own commits were replayed onto:
	// public main, R, the rebased tip of the features below it, or, for an
	// append to the feature's open PR, that PR's head H; "" when a stacked
	// gate stopped before the features below it were built.
	Base          string          `json:"base"`
	Overlay       string          `json:"overlay"`
	Candidate     string          `json:"candidate"`
	PublishedTree string          `json:"published_tree"`
	ScannedAs     string          `json:"scanned_as"` // the mode the leak scan covered: preserve scans every commit, squash and curated the final tree
	Commits       []CommitInfo    `json:"commits"`    // the replayed commits, oldest first; empty when the replay didn't finish
	Checks        string          `json:"checks"`
	FailedChecks  []string        `json:"failed_checks"`
	Leak          string          `json:"leak"`
	Findings      []leak.Finding  `json:"findings"`
	Waived        []WaivedFinding `json:"waived"`
	// RepoGuards is what the gate did with the public workflows' repo
	// guards in C; nil when no candidate was built.
	RepoGuards *workflow.GuardReport `json:"repo_guards"`
	// ShadowPR is the gate PR in the shadow repo; nil when none was opened
	// (the gate failed before CI, a dry run, or a reused gate this promote
	// didn't wait on).
	ShadowPR *int `json:"shadow_pr"`
	// Job names the gate's CI wait, for wait; nil when no candidate was
	// pushed (a leak, a conflict, a dry run, or a reused gate this promote
	// didn't wait on).
	Job *string `json:"job"`
	// Reused is set by promote when it reuses a fresh green gate instead of re-running it.
	Reused bool `json:"reused"`
	// Children and Chain place a stacked feature, with Parent: the features
	// stacked directly on it, and the unpublished stack from its bottom up
	// to it, which a promote of the stack lists. Both are empty, and Parent
	// is "", for a feature with neither a parent nor children; a bottom
	// feature with children has Parent "main" (stackView).
	Children []string `json:"children,omitempty"`
	Chain    []string `json:"chain,omitempty"`
	// Ready is the stack's readiness after a passing gate (F-12), for next;
	// empty for a feature on its own and for any other result.
	Ready   StackReadiness   `json:"-"`
	Preview *preview.Preview `json:"preview"`
	// Update is how the gate built the update of the feature's open PR
	// (append onto its head, or replace); absent for a first publish.
	Update *feature.GateUpdate `json:"update,omitempty"`
	// contains filtered or unexported fields
}

type GateSummary

type GateSummary struct {
	Checks    string    `json:"checks"`
	Leak      string    `json:"leak"`
	ScannedAs string    `json:"scanned_as"`
	Fresh     bool      `json:"fresh"`
	At        time.Time `json:"at"`
	Job       *string   `json:"job"` // non-nil exactly when the gate is pending: what wait takes
}

GateSummary is a feature's last gate result.

type GrantStatus

type GrantStatus struct {
	grant.Grant
	Expired          bool   `json:"expired"`
	RemainingSeconds *int64 `json:"remaining_seconds"`
}

type GroupAccess

type GroupAccess struct {
	Missing  []string `json:"missing"`
	Unknown  []string `json:"unknown"`
	Optional []string `json:"optional_missing"`
}

GroupAccess is what a token lacks for one command group, each entry "<permission> on <owner/repo>": Missing it doesn't have, Unknown GitHub won't say, and Optional ones the group can do without (sync's cancels, audit's secret names) that it doesn't have.

type InitOptions

type InitOptions struct {
	Public string
	Shadow string // default: <public>-shadow
	Adopt  bool
	// Schedule is sync.schedule for a new overlay: "" or config.ScheduleOff
	// leaves scheduled sync off, DefaultSyncSchedule turns it on. An
	// overlay that already exists keeps its config: init refuses with
	// BAD_FLAG, before pushing anything, if its schedule differs.
	Schedule string
}

type InitResult

type InitResult struct {
	Public           string           `json:"public"`
	Shadow           string           `json:"shadow"`
	CreatedShadow    bool             `json:"created_shadow"`
	Adopted          bool             `json:"adopted"`
	Resumed          bool             `json:"resumed"`
	MirroredBranches []string         `json:"mirrored_branches"`
	MirroredTags     []string         `json:"mirrored_tags"`
	OverlayCreated   bool             `json:"overlay_created"`
	DefaultBranchSet bool             `json:"default_branch_set"`
	Audit            *AuditResult     `json:"audit"`
	Preview          *preview.Preview `json:"preview"`
	// GuardInstalled: the push guard is installed in this checkout when
	// init finishes (installed by this run, or before it).
	GuardInstalled bool `json:"guard_installed"`
	// Doctor is doctor's summary after a guided init; nil otherwise.
	Doctor *DoctorSummary `json:"doctor"`
	// Guided and DoctorChecks are for human output only.
	Guided       bool          `json:"-"`
	DoctorChecks []DoctorCheck `json:"-"`
	// Ready is true once Init returned without an error; human output says
	// "ready" only then. It is not part of the JSON result.
	Ready bool `json:"-"`
}

type JobEvent

type JobEvent struct {
	SchemaVersion string    `json:"schema_version"`
	Type          string    `json:"type"`
	Job           string    `json:"job"`
	At            time.Time `json:"at"`
	// waiting
	Candidate   string `json:"candidate,omitempty"`
	ShadowPR    int    `json:"shadow_pr,omitempty"`
	ShadowPRURL string `json:"shadow_pr_url,omitempty"`
	CITimeout   string `json:"ci_timeout,omitempty"`
	// check
	Name       string `json:"name,omitempty"`
	Status     string `json:"status,omitempty"`
	Conclusion string `json:"conclusion,omitempty"`
	// settled
	Outcome      string   `json:"outcome,omitempty"`
	FailedChecks []string `json:"failed_checks,omitempty"`
}

JobEvent is one line of wait --json's NDJSON stream. Fields beyond the first four belong to one type and are omitted from the others.

type JobRef

type JobRef struct{ Feature, Candidate string }

JobRef is a parsed job id: gate/<feature>/<the candidate's first 12 hex>.

func ParseJob

func ParseJob(id string) (JobRef, error)

ParseJob parses a job id as gate --detach printed it.

type LogOptions

type LogOptions struct {
	Feature, Op, ActorPrefix, Pair string
	Since                          time.Time
	Limit                          int
}

LogOptions filter the ledger. Feature and Op match exactly, Pair (a public repo) ignoring case, ActorPrefix as a prefix; a zero Since is no limit, and Limit 0 is DefaultLogLimit (the CLI asks for 1 or more).

type LogResult

type LogResult struct {
	Entries []ledger.Entry `json:"entries"` // newest first, never null
}

func Log

Log reads this workspace's ledger, newest first. It needs no pair, no token and no network.

type ManagedFile

type ManagedFile struct {
	Path   string         `json:"path"`
	Status managed.Status `json:"status"`
}

ManagedFile is one Ghostbranch-managed overlay path and its state.

type PRSync

type PRSync struct {
	Behind  int `json:"behind"`
	Foreign int `json:"foreign"`
}

PRSync is how a published feature's open public PR compares with the feature: Behind counts the commits on the private branch the PR doesn't have (gate and promote bring them). Foreign counts the commits on the PR's branch Ghostbranch didn't publish (foreignCommits): a person pushed them on GitHub, and feature pull brings them in.

type PromoteOptions

type PromoteOptions struct {
	Feature string
	// Features is a stack, bottom first: promote a b c. One feature is the
	// same as Feature. Feature is always Features[0] once validated.
	Features []string
	// Modes and Titles set one branch's history mode and PR title in a
	// stack promote (--mode-for, --title-for), keyed by feature.
	Modes    map[string]string
	Titles   map[string]string
	Mode     string // squash, preserve or curated; "" means promote.default_mode
	Message  string
	Title    string
	Body     string
	Branch   string       // public branch name; defaults to the feature name
	Curate   string       // path to the --curate file (curated mode)
	Groups   []plan.Group // the --curate file's groups, as read (ValidatePromoteOptions loads them)
	PlanOnly bool
	Yes      bool
	Archive  bool // as ApplyOptions.Archive
	Detach   bool // gate and return the pending job without waiting on CI
}

type PromoteResult

type PromoteResult struct {
	Gate     *GateResult      `json:"gate"`
	Plan     *plan.Plan       `json:"plan"`
	PlanPath string           `json:"plan_path"`
	Findings []leak.Finding   `json:"findings"`
	Waived   []WaivedFinding  `json:"waived"`
	Applied  *ApplyResult     `json:"applied"`
	Preview  *preview.Preview `json:"preview"`
	// PRs is every PR the plan opens, bottom first, with numbers once
	// applied; StackNumber is the native GitHub stack, null when none.
	PRs         []plan.StackPR `json:"prs"`
	StackNumber *int           `json:"stack_number"`
}

type PullOptions

type PullOptions struct {
	Feature string
}

PullOptions are feature pull's inputs.

type PullResult

type PullResult struct {
	Feature string `json:"feature"`
	From    string `json:"from"` // the feature branch's head before
	To      string `json:"to"`   // its head now; From when it didn't move
	// Adopted are the PR's commits Ghostbranch didn't publish, now in the
	// feature, oldest first; never null.
	Adopted []feature.AdoptedCommit `json:"adopted"`
	// Children are the live features stacked directly on it, when it moved
	// (To != From): they sit on its old commits until feature restack
	// moves them; never null.
	Children []string         `json:"children"`
	Preview  *preview.Preview `json:"preview"`
}

PullResult is what feature pull brought in.

type RemoveResult

type RemoveResult struct {
	Public  string           `json:"public"`
	Shadow  string           `json:"shadow"`
	Grants  int              `json:"grants"`
	Waivers int              `json:"waivers"`
	Plans   []string         `json:"plans"`
	Dir     string           `json:"dir"`
	Preview *preview.Preview `json:"preview"`
}

RemoveResult is what remove took off this machine. Grants and Waivers count what was revoked; Plans are the plan files removed and Dir the pair's directory (mirror, work dirs, lock).

type RestackOptions

type RestackOptions struct {
	Feature string
	// Onto re-parents Feature: "main", or the feature to stack it on. Empty
	// restacks the features above Feature onto their parents' tips.
	Onto string
}

RestackOptions are feature restack's inputs.

type RestackResult

type RestackResult struct {
	Feature   string            `json:"feature"`
	Onto      string            `json:"onto"`      // "" when it restacked the features above Feature
	Restacked []RestackedBranch `json:"restacked"` // never null
	Pending   []string          `json:"pending"`   // not reached because it stopped, the one it stopped at first; never null
	Preview   *preview.Preview  `json:"preview"`
	// StoppedOnConflict is true when the restack stopped at Pending[0] on a
	// conflict (REBASE_CONFLICT), not on Ctrl-C or a git failure.
	StoppedOnConflict bool `json:"-"`
	// contains filtered or unexported fields
}

RestackResult is what a restack moved, in the order it pushed.

func (*RestackResult) GateFirst

func (r *RestackResult) GateFirst() string

GateFirst is the lowest branch, from the bottom of the restacked stack up, whose gate isn't fresh: the first one next gates. It is "" when nothing was restacked.

type RestackedBranch

type RestackedBranch struct {
	Feature  string `json:"feature"`
	Parent   string `json:"parent"`    // "main", or the feature it is stacked on now
	From     string `json:"from"`      // its head before
	To       string `json:"to"`        // its head now; From when only its record moved
	ForkedAt string `json:"forked_at"` // its fork point now; "" on main
}

RestackedBranch is one branch a restack moved, or whose record it moved.

type SecretRef

type SecretRef struct {
	Name  string   `json:"name"`
	Files []string `json:"files"`
}

SecretRef is one secret the workflows reference and the files that do.

type StackReadiness

type StackReadiness struct {
	// GateFirst is the lowest feature below this one whose gate isn't
	// fresh, or doesn't link to the level below it: gate it first. "" when
	// every one below is ready.
	GateFirst string
	// StaleAbove are the features stacked directly on this one whose gates
	// aren't fresh on this gate.
	StaleAbove []string
	// Levels is the longest stack from the bottom, up through this feature
	// and on through its only fresh child at each step, every level fresh
	// and linked by tree to the one below (trap 51). nil with GateFirst.
	Levels []string
	// TitleFor are the levels with more than one gate commit: their PRs
	// need --title-for.
	TitleFor []string
	// Live are the features stacked directly on this one that aren't
	// archived: a handed-off child's PR is the person's (R12).
	Live []string
}

StackReadiness is what next offers after a passing gate of a stacked feature (F-12). The engine works it out with the test promote uses (gateFresh and the tree link), so next never offers a promote that refuses stale_gate; the front end only reads it (directive 6).

type StatusResult

type StatusResult struct {
	Public        string          `json:"public"`
	Shadow        string          `json:"shadow"`
	PublicMain    string          `json:"public_main"`
	ShadowMain    string          `json:"shadow_main"` // "" when shadow main is missing
	ShadowBehind  bool            `json:"shadow_behind"`
	Overlay       string          `json:"overlay"`        // overlay commit
	OverlayStatus string          `json:"overlay_status"` // current | outdated (a managed file needs upgrade)
	ScheduledSync bool            `json:"scheduled_sync"`
	Features      []FeatureReport `json:"features"` // never null; abandoned included
}

StatusResult is the pair's health and every feature, abandoned included.

type SyncBranch

type SyncBranch struct {
	Name   string `json:"name"`
	From   string `json:"from"`
	To     string `json:"to"`
	Status string `json:"status"` // up_to_date | created | fast_forwarded | diverged | push_failed
}

SyncBranch is what sync did to one branch in sync.branches. From is "" when the shadow repo had no such branch.

type SyncOptions

type SyncOptions struct {
	NoCancel bool
	NoClose  bool
}

SyncOptions are sync's flags. NoCancel leaves the workflow runs the sync's pushes start in the shadow repo running. NoClose leaves merged features in place.

type SyncResult

type SyncResult struct {
	Public           string           `json:"public"`
	Shadow           string           `json:"shadow"`
	Action           string           `json:"action"`
	PublicMain       string           `json:"public_main"`
	ShadowMainBefore string           `json:"shadow_main_before"`
	ShadowMainAfter  string           `json:"shadow_main_after"` // where sync left shadow main; when its push failed, the SHA sync read
	Branches         []SyncBranch     `json:"branches"`
	CancelledRuns    []CancelledRun   `json:"cancelled_runs"`
	Closed           []ClosedFeature  `json:"closed"`          // merged features sync closed; never null
	Surveyed         bool             `json:"surveyed"`        // the features were surveyed: false with --no-close or when the survey failed
	ActiveFeatures   int              `json:"active_features"` // features not abandoned and not closable by sync; 0 unless Surveyed
	Attention        []FeatureReport  `json:"attention"`       // features a person must look at; never null
	Preview          *preview.Preview `json:"preview"`
}

type TokenCheck

type TokenCheck struct {
	Source     string `json:"source"`
	HTTPStatus int    `json:"http_status"`
}

type UpgradeFile

type UpgradeFile struct {
	Path               string         `json:"path"`
	Status             managed.Status `json:"status"`
	Action             string         `json:"action"` // add | update | remove | unchanged
	ReplacesLocalEdits bool           `json:"replaces_local_edits"`
}

UpgradeFile is one managed overlay path and what upgrade does to it.

type UpgradeResult

type UpgradeResult struct {
	Public  string           `json:"public"`
	Shadow  string           `json:"shadow"`
	Changed bool             `json:"changed"`
	Files   []UpgradeFile    `json:"files"`
	Commit  string           `json:"commit"`
	Audit   *AuditResult     `json:"audit"`
	Preview *preview.Preview `json:"preview"`
}

UpgradeResult is the upgrade command's data.

type WaitOptions

type WaitOptions struct {
	// Timeout returns the job still pending after this long; zero waits
	// until CI settles or the gate's own CI budget runs out.
	Timeout time.Duration
	// Events receives the job's events as they happen; nil for none.
	Events func(JobEvent)
}

WaitOptions are wait's flags.

type WaivedFinding

type WaivedFinding struct {
	leak.Finding
	Reason string `json:"reason"`
}

WaivedFinding is a finding a person waived, with their reason.

type WaiversAddOptions

type WaiversAddOptions struct {
	Feature string
	IDs     []string
	Reason  string
}

type WaiversAddResult

type WaiversAddResult struct {
	Feature string           `json:"feature"`
	Waivers []waiver.Waiver  `json:"waivers"`
	Preview *preview.Preview `json:"preview"`
}

type WaiversListResult

type WaiversListResult struct {
	Pair    string          `json:"pair"`
	Waivers []waiver.Waiver `json:"waivers"`
}

type WaiversRevokeResult

type WaiversRevokeResult struct {
	Revoked []waiver.Waiver  `json:"revoked"`
	Preview *preview.Preview `json:"preview"`
}

Jump to

Keyboard shortcuts

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