package index
// strata.go — RFC 0006: a stack of dependent landings, derived and never
// stored. Follow target → branch across landings: a landing whose target
// is another landing's branch is a segment stacked on it; the one whose
// target is a plain branch is the base. The chain is read fresh from the
// summaries every time, so a segment retargeted by zit#144 after its base
// lands simply stops being one.
// StackSegment is one rung of a strata as the record page draws it.
type StackSegment struct {
ID string `json:"id"`
Title string `json:"title"`
Branch string `json:"branch"`
Target string `json:"target"`
State string `json:"state"`
Position int `json:"position"` // 1 is the base
// Landable marks the one open segment whose target is not itself an
// open landing's branch — the only rung accept can act on.
Landable bool `json:"landable"`
// CI is this rung's own verdict, filled by the reader from the tag.
CI string `json:"ci,omitempty"`
}
// Strata is a chain, bottom-up.
type Strata struct {
Base string `json:"base"`
// Top is the highest rung: landing "the strata" means landing up
// to it.
Top string `json:"top"`
// CI is the strata's verdict: the top rung's. Its tip contains every
// rung below, so a run there validates the stack as a unit — the
// reading Graphite and Aviator settled on. A lower rung's own
// verdict still says what it says; this is the one to land on.
CI string `json:"ci,omitempty"`
Segments []StackSegment `json:"segments"`
// Broken names why the chain cannot be walked: a cycle, or a base
// that was rejected so everything above it is stacked on work
// nobody will land (RFC 0006 §An abandoned base is flagged).
Broken string `json:"broken,omitempty"`
}
// AttachStrata computes, for every OPEN landing in ls, which stack it
// belongs to and where: Stack (the base's id), Position and Depth. The
// input is every landing the caller knows about, whatever its state,
// because a segment's target may be the branch of a landing that was
// rejected — and that is a fact the chain must report, not lose.
//
// Superseded landings take no part: the current landing on a branch is
// the one the chain runs through.
func AttachStrata(ls []LandingSummary) {
byBranch := map[string]*LandingSummary{}
for i := range ls {
l := &ls[i]
if l.SupersededBy != "" {
continue
}
if cur, ok := byBranch[l.Branch]; !ok || l.State == "open" && cur.State != "open" {
byBranch[l.Branch] = l
}
}
for i := range ls {
l := &ls[i]
l.Stack, l.Position, l.Depth, l.Broken = "", 0, 0, ""
if l.State != "open" || l.SupersededBy != "" {
continue
}
// Walk down to the base, counting.
seen := map[string]bool{l.ID: true}
base, pos := l, 1
for {
below, ok := byBranch[base.Target]
if !ok {
break // a plain branch: this is the base
}
if seen[below.ID] {
l.Broken = "cycle"
break
}
if below.State != "open" {
if below.State == "rejected" {
l.Broken = "base rejected"
}
// Accepted and not yet retargeted: the rung below is
// done and this one is next. It counts as the base of
// what remains.
break
}
seen[below.ID] = true
base, pos = below, pos+1
}
if pos == 1 && !isStackedOn(byBranch, l) {
continue // standalone: no stack at all
}
l.Stack, l.Position = base.ID, pos
}
// Depth is the tallest position sharing a base.
depth := map[string]int{}
for i := range ls {
if ls[i].Stack != "" && ls[i].Position > depth[ls[i].Stack] {
depth[ls[i].Stack] = ls[i].Position
}
}
for i := range ls {
if ls[i].Stack != "" {
ls[i].Depth = depth[ls[i].Stack]
}
}
}
// isStackedOn reports whether some open landing targets l's branch.
func isStackedOn(byBranch map[string]*LandingSummary, l *LandingSummary) bool {
for _, other := range byBranch {
if other.State == "open" && other.Target == l.Branch && other.ID != l.ID {
return true
}
}
return false
}
// StrataOf assembles the chain a landing belongs to, bottom-up, for the
// record page's map. Empty for a standalone landing. ls must already
// carry AttachStrata's reading.
func StrataOf(ls []LandingSummary, id string) Strata {
var me *LandingSummary
for i := range ls {
if ls[i].ID == id {
me = &ls[i]
}
}
if me == nil || me.Stack == "" {
return Strata{}
}
out := Strata{Base: me.Stack, Broken: me.Broken}
for _, l := range ls {
if l.Stack != me.Stack {
continue
}
out.Segments = append(out.Segments, StackSegment{
ID: l.ID, Title: l.Title, Branch: l.Branch, Target: l.Target,
State: l.State, Position: l.Position, Landable: l.Position == 1 && l.Broken == "",
})
}
// Bottom-up, by position.
for i := 1; i < len(out.Segments); i++ {
for j := i; j > 0 && out.Segments[j].Position < out.Segments[j-1].Position; j-- {
out.Segments[j], out.Segments[j-1] = out.Segments[j-1], out.Segments[j]
}
}
if n := len(out.Segments); n > 0 {
out.Top = out.Segments[n-1].ID
}
return out
}
// Decisions counts what awaits a decision: every open landing that is
// not stacked on another open one. A stack of five is one decision —
// the base's — because nothing above it can be acted on until it is.
func Decisions(ls []LandingSummary) int {
n := 0
for _, l := range ls {
if l.State == "open" && l.SupersededBy == "" && (l.Stack == "" || l.Position == 1) {
n++
}
}
return n
}