Skip to main content

internal/report/report.go

internal/report · 322 lines · 20 declarations · source

This file carries the package documentation, rendered on the package page.

Declarations

type Fact

type Fact struct {
// Source identity is governance metadata. It is never rendered into model input.
SourceObservationID string
SourceRevision int64
// Entity IDs distinguish equal displayed names. They are projection identities, not protected
// data-subject keys; empty IDs retain compatibility with caller-supplied named-only material.
SubjectID string
ObjectID string
Subject string
Predicate string
Object string
Statement string
Quote string
}

Fact is one asserted relation inside a community, with the words behind it.

The quote travels with the statement because a report written from statements alone is a summary of summaries: the extractor already compressed a sentence into a triple, and compressing that again loses the thing a reader would check. What the model sees is what was said.

source

method Fact.Size

func (f Fact) Size() int

Size is what one fact costs a context, in characters.

source

method Fact.SubjectLabel

func (f Fact) SubjectLabel() string

Labels carry disambiguation into model input without changing the recorded statement or quote.

source

method Fact.ObjectLabel

func (f Fact) ObjectLabel() string

source

func entityLabel

func entityLabel(name, id string) string

source

type Report

type Report struct {
// Loaded from persisted dependency registrations when a child is substituted. Model-returned
// values are ignored by persistence; only the supplied context determines report provenance.
SourceObservationIDs []string
SourceRevisions map[string]int64
// Title names the subject, not the community. "The Dublin office move" rather than "Community 7".
Title string
// Summary is the paragraph a thematic answer is built from.
Summary string
// Importance is how much this subject appears to matter, 0 to 10, WITH the reason for it.
//
// The number alone would be a ranking signal nobody could check, and this system does not
// introduce a ranking stage without a measurement that justifies it. Carried together, it is a
// claim a reader can disagree with — which is what makes it usable now and replaceable later.
Importance float32
ImportanceReason string
// Findings are the specific things the subject consists of, each with the reasoning behind it.
Findings []Finding
}

Report is what a community says about itself.

Why a structure rather than prose

The parts are read separately. A title is what a caller sees in a list, a summary is what goes into a bundle, and the findings carry the claims that can be checked against the facts underneath. One blob would have to be re-parsed by everything that reads it, and a model asked for a blob returns a different shape each time — so the parse would be a guess repeated at every call site.

source

type Finding

type Finding struct {
Summary string
Explanation string
}

Finding is one claim a report makes, with the material behind it.

source

type Context

type Context struct {
// Entities are the members of the community, sorted.
Entities []string
// Facts are the relations among them.
Facts []Fact
// Children are reports of sub-communities that replaced their own raw material.
Children []Report
// Substituted is how many children were replaced, and Dropped how many facts did not fit even
// after every substitution. Reported rather than silent: a summary written from less than the
// whole subject is a different claim, and whatever reads this has to be able to say so.
Substituted int
Dropped int
}

Context is everything a report is written from.

Why child reports and raw facts are both here

A community too large to describe from its facts is described from what its parts already said. Both forms appear in one structure because the substitution is a property of a single context — some children replaced, some not — rather than two different kinds of request.

source

type Model

type Model interface {
Write(ctx context.Context, c Context) (Report, error)
}

Model writes a report from a context. It is a port for the same reason extraction's is.

source

type Writer

type Writer struct {
model Model
}

Writer produces reports. It holds a model and nothing else: there is no state a second report depends on, which is what makes a rebuild after an erasure produce the same thing as a first build.

source

func New

func New(model Model) *Writer

New builds a writer over a model.

source

method Writer.Model

func (w *Writer) Model() Model

Model returns the model this writer asks, so a caller can ask it who it is. Reports carry the identity of what wrote them, and the only thing that knows it is the model itself.

source

method Writer.Write

func (w *Writer) Write(ctx context.Context, c Context) (Report, error)

Write produces the report for one community.

A community with no facts is refused rather than described. There is nothing to write a summary from, and a model asked to describe a list of names will produce a paragraph that sounds like a subject — which is the one output that cannot be told apart from a real one downstream.

source

method Report.validate

func (r Report) validate() error

validate refuses a report that cannot do its job.

A report with no summary has nothing to put in a bundle, and one with no title is unusable in a list. An importance outside its range is a model that did not read the scale, and admitting it would put a number nobody can interpret next to a subject.

source

func BuildContext

func BuildContext(entities []string, facts []Fact, children map[string][]Fact,
childReports map[string]Report, budget int) Context

BuildContext assembles what a community's report is written from, within a budget.

Substitution rather than truncation

When the material does not fit, a child's raw facts are replaced by that child's REPORT, largest child first. Every subject stays present at lower resolution.

Truncating the facts instead would keep some subjects whole and drop others entirely, and which ones are dropped is an artefact of the order they happen to be in rather than of what matters. That failure is invisible from the output: the report reads perfectly and is about two thirds of a subject.

Largest first

The largest child frees the most room per substitution, so the fewest children are replaced. Each substitution costs resolution — a paragraph instead of the sentences behind it — so making as few as possible is making the report as detailed as the budget allows.

What happens when even that is not enough

Facts are dropped, and the count is reported. There is no arrangement of a fixed budget that fits an unbounded community, and a context that silently returned less would make the report a claim about the whole subject written from part of it.

source

func keep

func keep(facts []Fact, owner map[string]string, replaced map[string]bool) ([]Fact, int)

source

func sizeOf

func sizeOf(facts []Fact, children []Report) int

source

func largestUnreplaced

func largestUnreplaced(children map[string][]Fact, replaced map[string]bool,
reports map[string]Report) string

largestUnreplaced picks the child whose raw material is biggest, so one substitution frees the most. Ties break on the child's name, so the choice is the same on every rebuild.

source

func reportsOf

func reportsOf(reports map[string]Report, replaced map[string]bool) []Report

source

func factKey

func factKey(f Fact) string

factKey identifies a fact within a context. The quote is part of it: two relations between the same ends from different sentences are two pieces of material, and collapsing them would drop one.

source