"A boundary you can't compute is a boundary you don't have."
One Level Deep, cwk-Prefixed, Nothing Else
A hub that manages 'the family' needs an exact, mechanical answer to 'who is in the family?' Firelink's is deliberately narrow: direct child projects of one projects directory whose names begin with cwk, enumerated exactly one level deep. Not nested repositories. Not the retired attic and its descendants. Not adjacent non-cwk folders. Not the repository template. Not 'ancestors' or 'adjacent repos' that a roster document merely mentions.
This sounds almost too simple, and that's the point. A membership rule you can run in a loop is a rule that can't drift. The moment 'membership' depends on human judgment — 'well, that one kind of counts' — the census stops being reproducible and the hub starts lying about what it manages.
Prose Never Enrolls a Member
Firelink reads roster and network documents to decorate members with titles, one-liners, and lifecycle status. But those documents can also contain lineage notes, 'ancestors', and 'adjacent repos' sections written for humans. A critical rule: that prose never expands the managed set. A roster entry that sits outside the direct-project scope is reference material — not drift, not a card, not something to reconcile. The project root is the scope boundary; the documents decorate what's inside it and are powerless to enroll what's outside.
Why the Attic Stays Out
Retired projects live in an attic directory. They're still on disk, still have history, still matter as memory — and they are deliberately invisible to Firelink. If the census descended into the attic or followed nested repos, it would resurrect dead projects as live cards, probe ports that shouldn't exist, and turn 'we archived that' into 'why is this showing as missing?' The narrow scope is what lets Firelink honestly say 'this is the living family' without dragging every ghost along.