Skip to content

feat: inline ownership blocks (closes #3) - #179

Draft
zbedforrest wants to merge 4 commits into
mainfrom
feature/inline-ownership-oracle
Draft

feat: inline ownership blocks (closes #3)#179
zbedforrest wants to merge 4 commits into
mainfrom
feature/inline-ownership-oracle

Conversation

@zbedforrest

@zbedforrest zbedforrest commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Closes #3

Related PR(s)

Stacked on #178 (uses NewFromFileOwners and the same MergeCodeOwners merge path); only the top commit is new. Supersedes #45, which can be closed.

Related Issue(s)

Resolves #3. The issue floats AST-anchored comments (tree-sitter); this ships the language-agnostic line-range block form first, with AST anchoring noted in Future Features.

Summary / Background

Inline ownership: comment tags that mark a region of a source file as owned, so PR changes touching that region require the tagged owners' approval.

// <CO-inline={@alice,@security-team}>
func ValidateToken(token string) bool { ... }
// </CO-inline>

// and # prefixes work, tags are case-insensitive, and owners may be separated by commas or spaces. Owners inside one tag form an OR group, like a .codeowners line; overlapping blocks AND together. Enabled per-repo via enable_inline_ownership = true in codeowners.toml (default off).

Touched blocks become per-file reviewer requirements, AND-merged into .codeowners-derived ownership via codeowners.MergeCodeOwners (the same mechanism as require_both_branch_reviewers and oracle files), so inline ownership can only ever add requirements.

Design points:

  • Blocks are parsed from both the merge-base and head revisions of each changed file. Hunk coordinates come from the three-dot diff, so the base side reads at the merge base (git merge-base), not the base branch tip; otherwise line ranges drift whenever the base branch has advanced.
  • Renames are followed: the base revision is read under the old name, and a pure rename requires approval from all of the file's block owners. The ignore-dirs diff filter now keeps files whose old path is not ignored, so renaming a file into an ignored directory cannot hide it.
  • Tag lines are part of the protected range, so editing an ownership tag requires the owners recorded at base.
  • Fail-closed: a file that exists at a revision but cannot be read is a hard error. Zero-length hunks (pure insertion points) do not count as touching an adjacent block.

Code Changes

  • pkg/inlineowners: block parser + Requirements (block/hunk intersection)
  • internal/git: MergeBase helper; DiffFile.BaseHunks/BaseFileName; both-path ignore-dirs filter; changesSince carries base fields
  • internal/config: enable_inline_ownership (default false)
  • internal/app: applyInlineOwnership merge step reading base blocks at the merge base
  • README: "Inline Ownership" section; coverage badge regenerated
  • Tests: parser table tests (incl. CRLF, space/comma separators), requirement unit tests (rename, zero-length hunks, tag tampering, fail-closed reads), config parse test, diff-parsing tests for the new fields, and real-git e2e tests covering edit/tamper/delete/rename shapes plus an advanced-base-branch scenario

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces two major features to codeowners-plus: inline ownership, which allows comment tags inside source files to assign ownership to specific line ranges, and ownership oracles, which allow external tooling to feed computed ownership requirements via JSON files. The code review identified critical security vulnerabilities in the inline ownership implementation. Specifically, renaming a file allows a PR to bypass base-side inline ownership protections because the base reader queries the new filename instead of the original filename. Additionally, the implementation exhibits fail-open behavior by silently ignoring file read and scanner errors, which could allow attackers to bypass required reviews. The reviewer recommended tracking the original filename across renames and treating parsing/reading failures as hard errors to ensure a fail-closed design.

Comment thread pkg/inlineowners/inlineowners.go Outdated
Comment thread internal/git/diff.go
Comment thread pkg/codeowners/diff.go
Comment thread pkg/inlineowners/inlineowners.go Outdated
Comment thread pkg/inlineowners/inlineowners.go Outdated
@zbedforrest
zbedforrest force-pushed the feature/inline-ownership-oracle branch from bccb194 to e95f0d0 Compare July 22, 2026 19:26
@zbedforrest
zbedforrest force-pushed the feature/inline-ownership-oracle branch from e95f0d0 to 943d25b Compare July 22, 2026 20:10
@zbedforrest zbedforrest changed the title feature: inline ownership rules (closes #3) feat: inline ownership blocks (closes #3) Jul 22, 2026
@zbedforrest
zbedforrest force-pushed the feature/inline-ownership-oracle branch from 943d25b to 3f02855 Compare July 22, 2026 20:16
@zbedforrest
zbedforrest force-pushed the feature/inline-ownership-oracle branch from 3f02855 to 56d9e71 Compare July 22, 2026 20:39
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.

Inline ownership rules

1 participant