Skip to content

refactor: the vocabulary of a collected fact is a package (ADR 0065) - #103

Merged
kfeofantov merged 2 commits into
mainfrom
refactor/model-package
Sep 3, 2026
Merged

kfeofantov merged 2 commits into
mainfrom
refactor/model-package

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

internal/collector was three packages under one name: the vocabulary of
collected facts, the Kubernetes adapter that fills it in (informers, listers,
the kubelet poller), and the reductions that shrink an object to what ships.
Only the middle one needs a cluster — but journal, metadata, revisions,
inventory and sink all imported collector to name a type, and so linked
client-go to do window arithmetic.

47 plain-data types move to internal/model. It imports nothing beyond time
and reflect, and a test in the package asserts that: it is the whole point,
and nothing else in the build would fail if it stopped being true. collector
keeps the watchers, the poller, the filter and every reduction, and is now
imported only by cmd/agent.

Reasoning, alternatives (aliases, splitting collector by concern first) and
what was given up are in ADR 0065.

Result

package before after
journal, metadata, revisions 458 packages, 261 client-go 2, none
sink 486, 261 37, none
inventory / pprofprobe / targeting via collector 4 / 5 / 2, none
collector 458, 261 458, 261 — it is the adapter

collector: 5801 → 4946 lines, 95 → 48 exported declarations.

Why this is reviewable

It is a move, and that is checkable rather than asserted:

  • The golden payload bytes are byte-identical — git status internal/sink/testdata is clean.

  • Normalizing both trees (drop the moved blocks, drop the model. qualification
    and the import line, collapse whitespace) leaves four changed lines in the
    whole repository:

    internal/collector/nodes.go        prev.sameAs(info)   -> prev.SameAs(info)
    internal/collector/nodes_test.go   info.sameAs(want)   -> info.SameAs(want)
    internal/collector/policy.go       (r ResourceAmounts) empty() -> amountsEmpty(r)
    internal/collector/policy_test.go  one gofmt line-wrap, from a longer signature
    

Both renames are the same seam the package split follows, and both are stated in
the ADR.

Checks

$ go test -race ./...        # green, golden tests included
$ go vet -tags e2e ./...     # clean
$ golangci-lint run ./...    # 0 issues
$ make vulncheck chart-lint  # clean

e2e is running locally against kind; result to follow. No docs/security.md
change: nothing collected, stored or transmitted is different.

🤖 Generated with Claude Code

https://claude.ai/code/session_011YRKuL97KRndaopfpixrhp

kfeofantov and others added 2 commits September 3, 2026 23:20
internal/collector was three packages under one name: the vocabulary of
collected facts, the Kubernetes adapter that fills it in, and the reductions
that shrink an object to what ships. Only the middle one needs a cluster, but
every package that windows, aggregates or serializes a fact imported collector
to name a type — and so linked client-go.

The 47 plain-data types move to internal/model, which imports nothing beyond
time and reflect; a test in the package asserts that, because it is the point
and nothing else in the build would fail if it stopped being true. collector
keeps the watchers, the kubelet poller and every reduction, and is now imported
only by cmd/agent.

Two methods could not travel unchanged, along the same seam: NodeInfo.sameAs
became model.NodeInfo.SameAs (whether two views are the same fact is the type's
question), and ResourceAmounts.empty became amountsEmpty in collector (whether
a reduced set is worth emitting is the reduction's).

Transitive closure, before -> after:

    journal, metadata, revisions   458 packages (261 client-go) -> 2 (0)
    sink                           486 (261) -> 37 (0)
    inventory / pprofprobe / targeting        -> 4 / 5 / 2, none client-go
    collector                      458 (261), unchanged — it is the adapter

Provably a move: the golden payload bytes are unchanged, and after subtracting
the moved blocks and the model. qualification, the whole diff is the two
renames above plus one gofmt line-wrap.

go test -race ./... green; golangci-lint 0 issues.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YRKuL97KRndaopfpixrhp
`.gitignore` matched `coverage.*` in every directory, so `internal/model/coverage.go`
— the agent's own collection counters — was never added, and `git add -A` said
nothing. The tree built locally because the file was on disk; CI, which only has
what was committed, did not.

Anchoring the pattern to the root is what it meant: that is where the tooling
writes a profile, and a Go source file may legitimately be named coverage.go.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YRKuL97KRndaopfpixrhp
@kfeofantov
kfeofantov merged commit d41f157 into main Sep 3, 2026
3 checks passed
@kfeofantov
kfeofantov deleted the refactor/model-package branch September 3, 2026 20:43
kfeofantov added a commit that referenced this pull request Sep 4, 2026
…(ADR 0066) (#104)

Two places where input the agent does not trust decided something quietly.

The JWKS refetch. jwt.ParseWithClaims asks for the key before it checks the
signature, and the handler verifies before it reads the body — so the kid comes
from an unverified token and an unauthenticated caller chose how often the
controller queried its own API server, two GETs at a time, serialized behind one
mutex with a 10s ceiling each. Refreshes now happen at most once per 30s, and
the stamp is taken before the attempt so a failing refresh is bounded too. The
cost is a rotation picked up up to one node scan round late, on a payload that
is re-sent by the next pass.

The symbol policy. profiling.thirdPartySymbols was compared to "keep" and
everything else took the other branch, so "Keep" and every typo silently meant
"drop". It governs which frames may leave the node, so an unrecognized value now
fails the start, the same shape as ADR 0040's rule for an unset expectedSubject.

Also carries the .gitignore fix that PR #103 was meant to land and did not: an
unanchored `coverage.*` still matches a source file in any directory.

An existing test needed the clock advanced to keep proving its own claim: with
the floor in place, "the refresh failed" and "no refresh was attempted" look
alike from outside, so it now counts attempts on the source.

go test -race ./... green; golangci-lint 0 issues; chart-lint and vulncheck clean.


Claude-Session: https://claude.ai/code/session_011YRKuL97KRndaopfpixrhp

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant