agent build

This commit is contained in:
2026-08-28 12:21:44 +05:30
parent b6f8655909
commit f7df96c973
138 changed files with 24164 additions and 207 deletions

View File

@@ -0,0 +1,177 @@
package owliver
import (
"sort"
"github.com/krow/krow-backend/go-api/internal/domain"
)
// Context is the state of one organization's hiring, as counts.
//
// It is the answer to "what is actually going on here right now", read from
// PostgreSQL by service.SuggestionsService and handed to Highlights. Nothing in
// this package fetches it: the ranking stays a pure function of its inputs, and
// the one place that touches a database stays in the service layer where every
// other query lives.
//
// Counts rather than records, deliberately. A suggestion is a question, and
// deciding whether a question is worth asking needs to know that eleven
// candidates are waiting on a decision — never who they are. Nothing here can
// leak a name, and a zero-valued Context is a valid one: it means the reading
// has nothing to report, and Highlights answers with nothing rather than with a
// question about an empty set.
type Context struct {
// Positions.
ActivePositions int // status = 'active'
DraftPositions int // status = 'draft'
StarvedPositions int // active postings nobody has applied to
UnderfilledActive int // active postings with fewer hires than headcount
// Candidates, by where they are in the funnel.
Applications int
Unscreened int // status = 'applied' — nobody has scored them
Shortlisted int
Interviewing int
Hired int
FlaggedRisks int // interviews carrying at least one ai_flag
// Supply and the record behind it.
Staff int
Profiles int
UnverifiedProfiles int // profiles with no evidence filed
Courses int
ActivityEvents int
}
// Empty reports that nothing in this organization is worth remarking on.
//
// Used to tell "the database says there is nothing here" apart from "the
// database was not consulted": both produce no highlights, but only the second
// is a reason to fall back to anything.
func (c Context) Empty() bool { return c == Context{} }
/* ── Highlights ─────────────────────────────────────────────────────────── */
// Highlights is what is worth asking on this page given the state of the data.
//
// The counterpart to Suggest, and deliberately a separate function rather than
// a mode of it. Suggest answers "the user typed this, what did they mean" and
// is a pure string match; this answers "the user typed nothing, what should
// they know" and is a pure read of the organization. Merging them would make
// every keystroke pay for a database round trip in order to serve the one
// request per page that has nothing typed.
//
// The stages are the same and in the same order: page context, then permission,
// then relevance, then the cap. An intent the caller may not perform is never
// scored, so no ordering bug can surface one; an intent whose signal is zero is
// dropped rather than padded in, so a quiet workspace is offered nothing rather
// than three questions about empty sets.
func Highlights(page string, ctx Context, role domain.Role) []Suggestion {
out := []Suggestion{}
// Deny by default, exactly as Suggest does. An intent that reads nothing is
// permitted to every role, so without this an unparseable role would be
// offered the account readings.
if _, known := domain.ParseRole(string(role)); !known {
return out
}
intents, ok := catalogue[page]
if !ok {
return out
}
candidates := make([]scored, 0, len(intents))
for order, intent := range intents {
if !intent.permitted(role) {
continue
}
if intent.Signal == nil {
continue
}
signal := intent.Signal(ctx)
if signal <= 0 {
continue
}
candidates = append(candidates, scored{
suggestion: Suggestion{Text: intent.Text, Intent: intent.ID},
score: signal,
order: order,
onTopic: true,
})
}
// Strongest signal first; declaration order breaks every tie, so the same
// database state always produces the same three in the same sequence.
sort.SliceStable(candidates, func(a, b int) bool {
if candidates[a].score != candidates[b].score {
return candidates[a].score > candidates[b].score
}
return candidates[a].order < candidates[b].order
})
seenIntent := make(map[string]bool, MaxSuggestions)
for _, c := range candidates {
if len(out) == MaxSuggestions {
break
}
if seenIntent[c.suggestion.Intent] {
continue
}
seenIntent[c.suggestion.Intent] = true
out = append(out, c.suggestion)
}
return out
}
/* ── Signal helpers ─────────────────────────────────────────────────────── */
// maxTiebreak bounds the count half of a signal, so a very large organization
// cannot let a tie-break spill into the tier above it.
const maxTiebreak = 999
// when scores a reading as "how much does this matter when it is happening at
// all", with the count breaking ties inside a tier.
//
// The obvious formulation — weight × count — is wrong here, and wrong in a way
// that gets worse as an organization grows: a workspace with forty applications
// and one role nobody has applied to would be asked about the forty, because
// forty of anything outscores one of anything else. But the single starved role
// is the finding. Nobody needs to be told there are applications.
//
// So the tier dominates and the count only orders readings within it. `tier` is
// a judgement about the SUBJECT, made once where the intent is declared:
//
// 9 something is at risk and nobody is on it — a role with no applicants,
// an interview carrying a flag
// 7 work is queued on a person's decision — unscored candidates, unfinished
// drafts, unverified profiles
// 5 a state worth reviewing — a shortlist waiting, a role under-filled,
// recent hires
// 3 what exists, as a figure — headcounts, comparisons, trends
//
// A count of zero scores zero whatever the tier, which is what stops a page
// being asked an urgent-sounding question about an empty set.
func when(count, tier int) int {
if count <= 0 {
return 0
}
if count > maxTiebreak {
count = maxTiebreak
}
return tier*(maxTiebreak+1) + count
}
// atLeastTwo is the count, or zero below two.
//
// A comparison needs something to compare. "Which position has the strongest
// pipeline?" is not a question about a workspace holding one position, and
// "how is each department performing?" is not a question about one department —
// both would answer with a table of a single row, which is the padding
// Highlights exists to refuse.
func atLeastTwo(n int) int {
if n < 2 {
return 0
}
return n
}