Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "agentops",
"version": "3.8.0",
"description": "Engineering guidance for coding agents: behavior-driven planning, shared domain language, independent validation, and reusable improvements.",
"skills": "./skills-codex",
"skills": "./skills",
"interface": {
"displayName": "AgentOps",
"shortDescription": "Agent work you can verify and build on.",
Expand Down
8 changes: 3 additions & 5 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@ scripts/lib/probe-fixture-metadata.py text eol=lf

# Generated/derived artifacts — union-merge to kill textual re-conflicts during
# multi-PR drains (their canonical content is restored by scripts/regen-all.sh).
# Council 2026-06-06 (ag-bdg1). NOTE: cli/embedded/** is //go:embed'd and
# skills-codex/** ships in the tarball — they MUST stay committed; union-merge
# only avoids spurious conflict markers, regen produces the authoritative bytes.
# Council 2026-06-06 (ag-bdg1). NOTE: cli/embedded/** is //go:embed'd — it MUST
# stay committed; union-merge only avoids spurious conflict markers, regen
# produces the authoritative bytes.
registry.json merge=union
**/.agentops-generated.json merge=union
skills-codex/.agentops-manifest.json merge=union
docs/contracts/context-map.md merge=union
cli/docs/COMMANDS.md merge=union

Expand Down
24 changes: 0 additions & 24 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,6 @@ if [[ -n "$staged_embed" ]]; then
fi
fi

# --- Codex mirror delta check (when skills/ files are staged but skills-codex/ is not) ---
staged_skills_src=$(git diff --cached --name-only -- 'skills/*' 2>/dev/null || true)
if [[ -n "$staged_skills_src" ]]; then
affected_skills=$(echo "$staged_skills_src" | awk -F/ 'NF>=2 && $1=="skills" {print $2}' | sort -u)
drift_warned=0
for skill in $affected_skills; do
codex_dir="$REPO_ROOT/skills-codex/$skill"
[[ -d "$codex_dir" ]] || continue
staged_codex=$(git diff --cached --name-only -- "skills-codex/$skill/" 2>/dev/null || true)
if [[ -z "$staged_codex" ]]; then
if [[ "$drift_warned" -eq 0 ]]; then
echo "pre-commit: WARN — codex mirror parity drift detected:" >&2
drift_warned=1
fi
echo " - skills/$skill/ has staged changes but skills-codex/$skill/ does not." >&2
fi
done
if [[ "$drift_warned" -eq 1 ]]; then
echo " Run: bash scripts/regen-codex-hashes.sh # update hashes only" >&2
echo " Or: bash scripts/refresh-codex-artifacts.sh # full re-sync (regen + audit)" >&2
echo " Then: git add skills-codex/" >&2
fi
fi

# --- Dual changelog sync (auto-copy new release entries to docs/CHANGELOG.md) ---
if git diff --cached --name-only -- 'CHANGELOG.md' 2>/dev/null | grep -q '^CHANGELOG.md$'; then
if [[ -f "$REPO_ROOT/docs/CHANGELOG.md" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/fresh-install-conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ on:
- 'scripts/install*'
- 'scripts/fresh-install-conformance.sh'
- 'cli/**'
- 'skills-codex/**'
- 'skills/**'
- '.github/workflows/fresh-install-conformance.yml'
schedule:
# Nightly at 08:15 UTC (late evening Pacific) — a periodic drift detector.
Expand Down
6 changes: 0 additions & 6 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ jobs:
skills: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.skills }}
hooks: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.hooks }}
docs: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.docs }}
codex: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.codex }}
shell: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.shell }}
bats: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.bats }}
ci: ${{ steps.release.outputs.release == 'true' || steps.filter.outputs.ci }}
Expand Down Expand Up @@ -85,8 +84,6 @@ jobs:
- 'tests/windows/**'
skills:
- 'skills/**'
- 'skills-codex/**'
- 'skills-codex-overrides/**'
- 'tests/skills/**'
hooks:
- 'lib/**'
Expand All @@ -97,9 +94,6 @@ jobs:
- 'CHANGELOG.md'
- 'PRODUCT.md'
- 'SKILL-TIERS.md'
codex:
- 'skills-codex/**'
- 'skills-codex-overrides/**'
shell:
- '**/*.sh'
- 'scripts/**'
Expand Down
7 changes: 3 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ See [the skill menu](docs/SKILL-ROUTER.md) for optional guidance. Plan states ob
| Path | What it is |
|---|---|
| `cli/` | the Go `ao` tool |
| `skills/` | the shipped product; one `SKILL.md` contract per skill |
| `skills-codex/` | a generated projection of `skills/` — never hand-edit |
| `skills/` | the shipped product; one `SKILL.md` contract per skill, loaded directly by every runtime |
| `workflows/` | Claude Code workflow scripts |
| `scripts/check-*.sh` | the deterministic gates |
| `tests/` | bats suites |
Expand All @@ -41,7 +40,7 @@ cd cli && go build ./... && go vet ./... && go test ./...
During Go edits, run focused package tests and `bash scripts/check-go-lint.sh`
from the repository root before broad integration and final review. A passing Go test does not establish the repository's lint contract.

Run the gates with `ao gate check` (`--full` for the whole registry). Regenerate every metadata-owned projection — `skills-codex/` included — with
Run the gates with `ao gate check` (`--full` for the whole registry). Regenerate every metadata-owned projection with
`scripts/regen-all.sh` (`--check` to verify without writing); edit `skills/`, then regenerate. `tests/run-all.sh` is the local aggregate runner and must be green.
CI is authoritative (`.github/workflows/validate.yml`) and runs the bats suites as
`bats --jobs 4 --no-parallelize-within-files --print-output-on-failure tests/scripts/*.bats`, plus the Go bar above with `go test -race -shuffle=on ./...`.
Expand Down Expand Up @@ -238,7 +237,7 @@ AgentOps work ownership.
| RPI traversal or evidence-contract change | `docs/architecture/rpi-traversal.md`, `schemas/*.schema.json` |
| CLI command or flag | `cli/cmd/ao/` composition, `cli/internal/commands/<family>/` implementation, then generated `cli/docs/COMMANDS.md` |
| Skill behavior or inventory | `skills/<slug>/SKILL.md`, generated `docs/SKILL-ROUTER.md` |
| Codex projection | `docs/contracts/codex-skill-api.md`, `skills-codex-overrides/catalog.json` |
| Codex skill loading or invocation policy | `docs/contracts/codex-skill-api.md`, `skills/<slug>/agents/openai.yaml` |
| Deterministic checks | `docs/CI-CD.md`, `cli/internal/gates/` |

## Closeout
Expand Down
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- The Codex plugin now loads `skills/` directly. `.codex-plugin/plugin.json`
ships `./skills`, the same tree `ao skills link` and `npx skills` already
install, instead of a generated copy. Skill names, descriptions and bodies are
unchanged. Codex plugin users should refresh the marketplace and re-add the
plugin. Checked against codex-cli 0.156.1: a plugin install and a linked
install each load all 28 skills with no load errors.
- `interview` now carries its Codex invocation policy in
`skills/interview/agents/openai.yaml`. The generator used to derive that file
from `disable-model-invocation: true`; it is now hand-maintained in each
explicit-only skill, and `scripts/validate-codex-api-conformance.sh` fails when
one is missing or does not parse.
- `scripts/validate-codex-api-conformance.sh` checks `skills/` against what the
Codex loader enforces (unique frontmatter keys, a non-empty description, a
name of at most 64 characters, no nested `SKILL.md`) and the explicit-only
policy. It no longer enforces the portable Agent Skills field allowlist.
- `ao skills check` audits `skills/` only. Its JSON no longer has `parity_drift`
or a per-skill `codex_parity`, and `--strict` fails on errors alone.
`skills/catalog.json` no longer has `codex_override_present`.
- The `skill-eval` fixtures moved from `skills/_fixtures/` to
`tests/fixtures/skill-eval/`. Codex loads every `SKILL.md` under the plugin's
skill tree, so the fixtures would have shipped as a skill and a load error.

- Install narrowed to three paths: the Claude Code plugin, the Codex plugin, and
`npx skills@latest add boshu2/agentops` for every other agent (Cursor,
OpenCode, Gemini CLI/Antigravity, Pi, Grok Build, OpenClaw). Grok Bot takes
Expand All @@ -18,6 +40,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- The generated Codex copy of the skills: `skills-codex/` (278 files) and
`skills-codex-overrides/catalog.json`, with the generator and everything that
only policed the copy. Gone: `scripts/codex-sync.sh`, `regen-codex-hashes.sh`,
`register-new-codex-skill.sh`, `append-codex-override-entry.sh`,
`mirror-codex-references.sh`, `refresh-codex-artifacts.sh`,
`audit-codex-parity.{py,sh}`, `check-codex-parity-drift.sh`,
`lint-codex-native.sh`, `smoke-test-codex-skills.sh`,
`export-claude-skills-to-codex.sh`, the `validate-codex-generated-*`,
`-install-bundle`, `-override-coverage`, `-runtime-sections` and
`-skill-parity` validators, `scripts/lint/`, their tests, and the gates
`skill.codex-parity-drift`, `skill.codex-runtime-sections`,
`skill.codex-override-coverage` and `skill.codex-generated-artifacts`.
`scripts/regen-all.sh` no longer takes `--skills`, and
`scripts/test-ci-deterministic-gates.sh` no longer takes `--skip-codex`.
- What Codex plugin users lose with the copy. Each skill's `prompt.md`,
`.agentops-generated.json` and the `.agentops-manifest.json` inventory are no
longer shipped; Codex did not read them (the string `prompt.md` does not occur
in the codex-cli 0.156.1 binary, and skills load without it). The plugin now
ships the full AgentOps frontmatter instead of only `name` and `description`;
Codex ignores the extra fields, but the packages are no longer strict portable
Agent Skills frontmatter. Skill text is shipped as written: the generator's
rewrites (`Claude Code` to `Codex`, `~/.claude` to `~/.codex`, `/skill` to
`$skill`) no longer run. They changed nothing in the current 28 bodies.
- `ao doctor` no longer has the `fm-skills-stale-codex-sync` failure mode, and
its fixers can no longer write to `~/.codex/plugins/cache/agentops-marketplace`
or `~/.codex/.agentops-codex-install.json`.

- Bundled Flywheel tool skills (`account-rotation`, `agent-mail`, `cass`, `cc-hooks`, `dcg`,
`ms`, `ntm`, `rch`, `sbh`, `using-flywheel`) and their generated Codex copies.
Obtain tools and skills from their upstream authors; see the README
Expand Down
2 changes: 1 addition & 1 deletion PROGRAM.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ authority. Repository Git and release procedures remain separate.

- product and doctrine: `README.md`, `PRODUCT.md`, `GOALS.md`, `PROGRAM.md`, `AGENTS.md`;
- implementation: `cli/**`, `skills/**`, `schemas/**`, `scripts/**`, `tests/**`;
- generated projections: `skills-codex/**`, registries, routers, maps, CLI docs;
- generated projections: registries, routers, maps, CLI docs;
- repository checks and docs: `.github/workflows/**`, `docs/**`, `evals/**`.

Secrets, credentials, user configuration outside the repository, production
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/ao/workflows_composition.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func init() {
// newWorkflowsCommand wires the workflows command module to its host seams.
// The global --dry-run flag drives link/unlink; checkout resolution, target
// resolution, and the link/unlink filesystem sweeps are host effects delegated
// to internal/workflowsapp. Workflows are a Claude-only runtime adapter (the
// skills-codex doctrine, Claude-side), grouped under Knowledge next to skills.
// to internal/workflowsapp. Workflows are a Claude-only runtime adapter,
// grouped under Knowledge next to skills.
// Like skills, the family attaches no capabilities contract.
func newWorkflowsCommand() *cobra.Command {
module := workflowscommands.NewModule(clicontract.HostOptions{
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/ao/workflows_composition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestWorkflowsCompositionRegistersExactlyOneRootOwner(t *testing.T) {
func mkWorkflowsFixtureCheckout(t *testing.T, scripts ...string) string {
t.Helper()
root := t.TempDir()
for _, d := range []string{"skills", "skills-codex", "workflows"} {
for _, d := range []string{"skills", "workflows"} {
if err := os.MkdirAll(filepath.Join(root, d), 0o755); err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions cli/docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ ao provenance verify-verdict [flags]

### `ao skills`

Tooling for the skills/ source-of-truth and its skills-codex/
Tooling for the skills/ source-of-truth, the one tree every runtime

```
ao skills [command]
Expand Down Expand Up @@ -1051,7 +1051,7 @@ ao skills build <from-scratch|from-template|absorb-external> <slug> [flags]

#### `ao skills check`

Walk skills/ and skills-codex/, validating each skill's YAML
Walk skills/, validating each skill's YAML frontmatter (name +

```
ao skills check [flags]
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/commands/skills/link_selection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
func selectionFixture(t *testing.T) string {
t.Helper()
root := t.TempDir()
for _, dir := range []string{"skills/alpha", "skills/beta", "skills/not-a-skill", "skills-codex"} {
for _, dir := range []string{"skills/alpha", "skills/beta", "skills/not-a-skill"} {
if err := os.MkdirAll(filepath.Join(root, dir), 0o755); err != nil {
t.Fatal(err)
}
Expand Down
46 changes: 17 additions & 29 deletions cli/internal/commands/skills/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ func (m *Module) Command() *cobra.Command {
Use: "skills",
Short: "Inspect and validate the skills/ tree",
GroupID: "knowledge",
Long: `Tooling for the skills/ source-of-truth and its skills-codex/
parity sibling. Subcommands surface health (frontmatter completeness,
broken reference links, codex parity drift) without mutating either
tree.`,
Long: `Tooling for the skills/ source-of-truth, the one tree every runtime
loads. Subcommands surface health (frontmatter completeness, broken
reference links) and query the catalog without mutating skills/; link
and unlink change only runtime skill directories.`,
}
root.AddCommand(m.checkCommand())
root.AddCommand(m.buildCommand())
Expand All @@ -136,15 +136,13 @@ tree.`,
func (m *Module) checkCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "check",
Short: "Audit skills/ frontmatter, references, and codex parity",
Long: `Walk skills/ and skills-codex/, validating each skill's YAML
frontmatter (name + description present, name matches dir), checking
that every references/*.md is linked from SKILL.md (and vice versa),
and reporting parity drift against skills-codex/.
Short: "Audit skills/ frontmatter and references",
Long: `Walk skills/, validating each skill's YAML frontmatter (name +
description present, name matches dir) and checking that every
references/*.md is linked from SKILL.md (and vice versa).

Exits 0 by default. With --strict, exits 1 if any finding (missing
frontmatter, broken reference, parity drift) is reported, suitable for
CI gating.`,
frontmatter, broken reference) is reported, suitable for CI gating.`,
RunE: m.runCheck,
}
cmd.Flags().BoolVar(&m.checkJSON, "json", false, "Emit machine-readable JSON")
Expand All @@ -154,10 +152,8 @@ CI gating.`,
}

func (m *Module) runCheck(cmd *cobra.Command, _ []string) error {
skillsDir, codexDir := skillsapp.ResolveSkillsRoots()
opts := skillshealth.Options{
SkillsDir: skillsDir,
CodexDir: codexDir,
SkillsDir: skillsapp.ResolveSkillsRoot(),
OnlySkill: m.checkOnly,
Strict: m.checkStrict,
}
Expand All @@ -175,8 +171,7 @@ func (m *Module) runCheck(cmd *cobra.Command, _ []string) error {
fmt.Fprintf(out, "Skills audit (%s)\n", report.Generated)
fmt.Fprintf(out, "================\n")
fmt.Fprintf(out, "Skills audited: %d\n", len(report.Skills))
fmt.Fprintf(out, "Errors: %d\n", len(report.Errors))
fmt.Fprintf(out, "Parity drift: %d\n\n", len(report.ParityDrift))
fmt.Fprintf(out, "Errors: %d\n\n", len(report.Errors))

if len(report.Errors) > 0 {
fmt.Fprintln(out, "Errors:")
Expand All @@ -185,22 +180,15 @@ func (m *Module) runCheck(cmd *cobra.Command, _ []string) error {
}
fmt.Fprintln(out)
}
if len(report.ParityDrift) > 0 {
fmt.Fprintln(out, "Codex parity drift:")
for _, e := range report.ParityDrift {
fmt.Fprintf(out, " - %s\n", e)
}
}
if len(report.Errors) == 0 && len(report.ParityDrift) == 0 {
if len(report.Errors) == 0 {
fmt.Fprintln(out, "All skills healthy.")
}
}

if m.checkStrict && (len(report.Errors) > 0 || len(report.ParityDrift) > 0) {
if m.checkStrict && len(report.Errors) > 0 {
// Use SilenceUsage to avoid printing usage on this expected non-zero exit.
cmd.SilenceUsage = true
return fmt.Errorf("skills check failed: %d errors, %d parity-drift",
len(report.Errors), len(report.ParityDrift))
return fmt.Errorf("skills check failed: %d errors", len(report.Errors))
}
return nil
}
Expand Down Expand Up @@ -230,7 +218,7 @@ suitable for a CI dedup gate.`,
}

func (m *Module) runResolve(cmd *cobra.Command, _ []string) error {
skillsDir, _ := skillsapp.ResolveSkillsRoots()
skillsDir := skillsapp.ResolveSkillsRoot()
report, err := skillsresolve.Resolve(skillsresolve.Options{SkillsDir: skillsDir})
if err != nil {
return err
Expand Down Expand Up @@ -313,7 +301,7 @@ func (m *Module) runFind(cmd *cobra.Command, args []string) error {
}

query := joinArgs(args)
skillsDir, _ := skillsapp.ResolveSkillsRoots()
skillsDir := skillsapp.ResolveSkillsRoot()
metas, err := skills.Load(skillsDir)
if err != nil {
cmd.SilenceUsage = true
Expand Down Expand Up @@ -653,7 +641,7 @@ func (m *Module) runUnlink(cmd *cobra.Command, _ []string) error {

// loadCatalogOrErr loads skills/catalog.json with a remediation hint on failure.
func (m *Module) loadCatalogOrErr(cmd *cobra.Command) (*skills.Catalog, error) {
skillsDir, _ := skillsapp.ResolveSkillsRoots()
skillsDir := skillsapp.ResolveSkillsRoot()
cat, err := skills.LoadCatalog(skillsDir)
if err != nil {
cmd.SilenceUsage = true
Expand Down
Loading
Loading