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.
method Fact.Size
func (f Fact) Size() int
Size is what one fact costs a context, in characters.
method Fact.SubjectLabel
func (f Fact) SubjectLabel() string
Labels carry disambiguation into model input without changing the recorded statement or quote.
method Fact.ObjectLabel
func (f Fact) ObjectLabel() string
func entityLabel
func entityLabel(name, id string) string
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.
type Finding
type Finding struct {
Summary string
Explanation string
}
Finding is one claim a report makes, with the material behind it.
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.
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.
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.
func New
func New(model Model) *Writer
New builds a writer over a model.
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.
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.
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.
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.
func keep
func keep(facts []Fact, owner map[string]string, replaced map[string]bool) ([]Fact, int)
func sizeOf
func sizeOf(facts []Fact, children []Report) int
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.
func reportsOf
func reportsOf(reports map[string]Report, replaced map[string]bool) []Report
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.