establishing-code-ownership
Establishing code ownership
Two sources, checked in order:
products/*/product.yaml: source of truth underproducts/. Lists owning team(s) underowners:as bare slugs (the CODEOWNERS handle minus@PostHog/:conversations,logs,team-self-driving, …) and owns all ofproducts/<name>/**. Lives in the dir it owns, so never stale..github/CODEOWNERS+CODEOWNERS-soft: backup for paths outsideproducts/.CODEOWNERSis hard/blocking (mostly infra);CODEOWNERS-softcarries most product mappings for shared code (backend, frontend scenes, generated artifacts, overrides).
Fast path: ownership.js
ownership.js automates the deterministic repo-file resolution for both lookups. It finds the repo root from its own location (cwd- and worktree-independent) and enumerates tracked files via git ls-files. Run it first, then fall back to the manual reasoning below for what it can't resolve.
S=.agents/skills/establishing-code-ownership/ownership.js
node $S file posthog/hogql/printer.py # who owns this file (pass --all to see every matching CODEOWNERS rule)
node $S team team-surveys # every tracked file the team owns (slug or @PostHog/ handle both work)
node $S unowned # every tracked file with no owner (append path prefixes to scope, e.g. `unowned products/ frontend/`)
For glob matching it uses the vendored, GitHub-faithful matcher .github/scripts/codeowners.js (a zero-dependency JS port of hmarr/codeowners), the same matcher the reviewer auto-assigner runs in CI, so answers track what CI assigns rather than a separate reimplementation. That matcher reproduces GitHub's own CODEOWNERS rules: a slash-free pattern matches at any depth, a trailing slash matches a directory and its contents, and dir/* matches only direct children. So a CODEOWNERS-soft entry the script shows as unowned is genuinely dead — both CI and GitHub skip it too, not a quirk of a stricter local matcher.