Ambient facts about this repo and its family, returned as structured data: `identity` (repo, default branch, the trigger labels), `topology` (which repo fills the `code` / `issues` / `releases` / `learnings` role), and `lines` (the live version lines, which is primary, and the port direction). The framework default detects what it can and reads a repo's declared facts; a repo that has facts detection cannot reach ships its own `ops-repo-meta` instead. Called by name with (action, context-json) . NOT for direct use — never select it from a description match.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Instruções da origem · Visualização somente leitura
name
ops-repo-meta
description
Ambient facts about this repo and its family, returned as structured data: `identity` (repo, default branch, the trigger labels), `topology` (which repo fills the `code` / `issues` / `releases` / `learnings` role), and `lines` (the live version lines, which is primary, and the port direction). The framework default detects what it can and reads a repo's declared facts; a repo that has facts detection cannot reach ships its own `ops-repo-meta` instead. Called by name with (action, context-json) . NOT for direct use — never select it from a description match.
ops-repo-meta
The data capability. Every other capability and loop asks it "what is true about this
repo?" instead of detecting, guessing, or holding a config key.
Visibility: cross-cutting (read). Any layer may call it — a loop, a service, a
supporting primitive. It has no side effects.
Kind: data. Per the conformance spec's §4.4 carve-out, the structure is the
deliverable: every action MUST return well-formed structured data, not prose. A caller
parses the object; it does not read a sentence.
Invocation
ops-repo-meta <action> '<context-json>'
Action
Context
Returns
identity
(none)
{ repo, default_branch, labels }
topology
(none)
{ repos: { <role>: "owner/name" } }
lines
(none)
{ live, primary, port_order }
All three take no context. An absent context is {}, never an error. — do not guess, do not silently succeed. All three are reads, so
idempotency is free.
Reject any
action not in that table
Where the facts come from
In this order. Stop at the first that answers.
A repo-owned skill. If the repo ships its own ops-repo-meta, this one is not running
at all — that is what overriding means. Only needed for behaviour; for facts, use 2.
The repo's declared facts — .claude/ops-repo-meta.json, shaped by
scripts/ops-repo-meta.schema.json and checked by
scripts/validate-repo-meta.sh. This is the normal way a
repo answers.ops-install writes it; every key is optional, so a single-repo project on
one line needs no file at all. Read it, don't re-derive it.
Detection.detect.sh (in the ops-install plugin) reads git branches and repo
settings and returns source, branching.*, ci.provider, release.*, stack. Use it
for identity.repo, identity.default_branch, and as the seed for lines.
The framework defaults in this file.
This is not the old config file..claude/ai-ops.yml bound the engine through skill
pointers and held facts that now have exactly one owner each — branch model, base, release
base and merge strategy are private to ops-branching, the CI provider is internal to
ops-ci, and a capability is found by its name. That file is deleted, and
ops-repo-meta.json is a strict subset of what it held: only facts nothing can detect and
nothing else owns. The schema sets additionalProperties: false at every level so an
attempt to put the rest back fails loudly rather than drifting.
Topology in the file reads "what this repo is not." A role left out resolves to the repo
the file lives in. So a code repo whose issues live elsewhere declares issues, and the
issues repo declares code — which is the only place the code repo can be named, because
detection there would read the issues repo's own remote. route-event.sh reads exactly that
key at the edge to decide which repo a routine should work in.
Labels are keyed by purpose, never by name. A caller asks for
labels.land, not for the string ops/auto-merge — which is what lets a repo rename a
label without touching a loop. The values above are the framework defaults; every engine
label is namespaced ops/ (see CLAUDE.md). A repo that has already got a label meaning
"ready" points labels.ready at it instead of creating a second one.
in_progress and done are not two names for one thing.in_progress is state — on
while a loop is working the issue, off the moment it stops, so an interrupted run is findable.
done is provenance — a loop built this, which stays true forever. They shared one slot
until a live routine read labels.in_progress, found it pointed at the finished label, and
invented a name of its own (29-07-2026).
repo and default_branch come from detection. Note that the default branch is not
necessarily a branch work happens on — see lines.
Roles are code (required), issues, releases, learnings.
An unspecified role resolves to code. So a single-repo project returns only code,
and every role collapses onto it. Return what you know; the caller applies the fallback.
learnings is not automatically issues: where issues are public and code is not, a
proto-learning is an internal note and belongs with the code.
Operation → role, which is normative (conformance §7.3):
Operation class
Role
Read / label / close issues; the release trigger issue
issues
Branch, push, PR, CI status, PR labels, merge, tag
code
Publish / release realization
releases
File / label / close proto-learnings
learnings
Framework default: every role is the detected code repo, i.e. a single-repo project.
live — every line taking work now, ordered oldest first. A repo can have several
at once, which is why a caller must treat "the integration branch" as set membership,
never equality with one branch.
primary — the line work starts on before being ported. Not necessarily the
newest line, and not necessarily the default branch.
port_order — upward (ports travel toward the end of live) or downward (toward
the start).
The order of live is part of the answer.port_order gives a direction; only this array
says which lines lie in that direction. Without the order a caller has to compare version
numbers out of the names to work out what "newer" means, which is the caller learning a
product's naming scheme and breaks on the first line that is not vN. Return them in age
order, and a caller can find the lines on either side of any line by position alone.
A reversed live is silent, so validate-repo-meta.sh warns about it. Get the order wrong
and ops-port-loop finds zero targets for every change on the primary line — a legitimate
outcome it reports and stops on, so nothing errors and the lines simply drift apart. That is a
real bug, not a hypothetical: onboarding seeded live from a newest-first detection and shipped
a backwards array (29-07-2026). Ordering cannot be a hard error, because a line name is an
arbitrary string with no general notion of older, so when every name is a bare vN the validator
warns on a descending list and still passes.
Naming the line a PR is on is not an action here. It is not derivable from this data: the
mapping from a branch to a line is ops-branching's private business, and ops-branching is
command-only. So the caller that created the PR is the one that knows, and it passes the line
along. A caller that has only a PR and must name its line may match the base ref against these
line names, but only on an exact single match, and it MUST stop and ask a human otherwise
rather than pick the closest.
None of this is derivable from version numbers or from the default branch, so a repo with
more than one live line must declare it — in .claude/ops-repo-meta.json, which is source 2
above and which this framework default reads. Declaring is not overriding. You only ship your
own ops-repo-meta when the behaviour has to differ, such as facts that change per run.
Guessing "newest = primary" picks wrong, which is why the file exists.
Callers MUST read this every time and MUST NOT cache it. A major-version cutover adds a
line, and that must not require an engine change.
Framework default: a single unnamed line —
{ "live": ["default"], "primary": "default", "port_order": "upward" } — meaning the repo
has no version lines and the default branch is the whole story. ops-branching reads that
as "one integration branch".
Rules
Return data, never prose. A caller parses this. A sentence is a bug.
Never invent a fact. If something is genuinely unknown, omit the key rather than
guessing — an omitted role resolves to code, and an omitted line list is the
single-line default. A guessed primary line silently sends work to the wrong branch.
No side effects. All three actions are reads. Never create a label, branch or file.
Detection is a seed, not an authority, for anything a human had to decide: the
primary line and the port order are declared facts by definition.