Skip to content

docops upgrade + update-check + read-side commands (TP-011/012/018/020/021)#1

Merged
nachiket-p merged 11 commits into
mainfrom
dev
Apr 23, 2026
Merged

docops upgrade + update-check + read-side commands (TP-011/012/018/020/021)#1
nachiket-p merged 11 commits into
mainfrom
dev

Conversation

@nachiket-p
Copy link
Copy Markdown
Member

@nachiket-p nachiket-p commented Apr 22, 2026

Summary

Ships the in-band upgrade path for projects already initialized with docops, plus a gstack-shaped update probe so users learn when their binary is stale, plus CLAUDE.md support so Claude Code reads the docops invariants by default, plus the four read-side commands that landed earlier on dev.

  • TP-018docops upgrade: refresh DocOps-owned scaffolding (skills, schemas, AGENTS.md/CLAUDE.md blocks) without clobbering docops.yaml or the pre-commit hook. Opt-in --config / --hook for the customizable bits. Manifest safety belt (.docops-manifest) refuses to silently delete user-added files inside the docops-owned subdirs on subsequent runs.
  • TP-021docops update-check: cached probe of upstream VERSION, fail-quiet on transient errors, 6h/24h split TTLs, snooze with escalating windows, dev-build / DOCOPS_UPDATE_CHECK=off skips. Wired into docops upgrade as a pre-flight warning so you cannot accidentally sync the older templates.
  • TP-022 — Ship CLAUDE.md alongside AGENTS.md: both files share the same docops block. init writes both; upgrade refreshes both and creates either one if absent, so v0.1.x users gain CLAUDE.md on first post-ADR-0024 upgrade. A templates-sync test catches block drift at build time. Closes the gap where Claude Code reads CLAUDE.md by default and never saw the docops invariants.
  • TP-020 — pure refactor: extract internal/scaffold/ from internal/initter/ so upgrader and initter share helpers. No behavior change for init.
  • TP-011 / TP-012docops search, docops get, docops list, docops graph, docops next (already on dev, included in this PR).

Planning artifacts: ADR-0023 (update-check), ADR-0024 (CLAUDE.md), TP-020/021/022.

New CLI surface

docops upgrade [--dry-run] [--yes] [--config] [--hook] [--json]
docops update-check [--force] [--snooze] [--json]

docops upgrade output uses the sigils promised by ADR-0021:
+ new, ~ refreshed, - removed, = up-to-date, ~ block-refreshed.

Files of note

  • internal/scaffold/ — shared planner helpers (new package)
  • internal/upgrader/ — TP-018 implementation + manifest safety belt
  • internal/updatecheck/ — TP-021 implementation
  • cmd/docops/cmd_upgrade.go, cmd/docops/cmd_update_check.go — CLI surface
  • templates/CLAUDE.md.tmpl — new sibling of AGENTS.md.tmpl, shared docops block
  • templates/agents_claude_block_sync_test.go — fails the build if the two block bodies drift
  • templates/skills/docops/upgrade.md — shipped skill
  • VERSION — upstream source of truth (initially 0.1.1); release-time bump documented but not yet automated in goreleaser
  • README.md — new "Upgrading an existing project" subsection
  • templates/AGENTS.md.tmpl — adds upgrade and update-check to the CLI list, footer mentions CLAUDE.md sibling

Test plan

  • go test ./... — all packages green (15 pkgs)
  • internal/upgrader/upgrader_test.go — fresh idempotent run, add+refresh+remove cycle, --config / --hook gating, first-run vs subsequent-run safety belt, AGENTS.md and CLAUDE.md block refresh, CLAUDE.md created when absent, dry-run writes nothing
  • internal/updatecheck/updatecheck_test.go — cache hit (UP_TO_DATE / UPGRADE_AVAILABLE) skips network, stale cache refetches, network error → up-to-date, invalid-response cases, dev-build skip, env opt-out, snooze suppression + version-bump invalidation + expiry, --force bypass
  • cmd/docops/cmd_upgrade_test.go, cmd/docops/cmd_update_check_test.go — CLI exit codes, JSON shape, --yes flow
  • internal/scaffold/scaffold_test.go — direct coverage for moved helpers
  • templates/agents_claude_block_sync_test.go — block bodies stay byte-identical across the two templates
  • Manual smoke: scaffold a fresh project in /tmp with init --yes, both AGENTS.md and CLAUDE.md present; delete CLAUDE.md, run upgrade --yes, CLAUDE.md restored from template
  • Manual smoke: seed a stale skill, dry-run + apply, drop a custom file → safety belt fires (exit 2)
  • docops upgrade --dry-run against this repo: identifies real drift correctly (refresh several skills, add refresh.md + upgrade.md, remove next.md, refresh AGENTS.md block, create CLAUDE.md) — left as a separate refresh step for a later commit

Follow-ups (not in this PR)

  • Wire goreleaser to bump VERSION from the git tag at release time
  • Run docops upgrade --yes against this repo to sync its own scaffolding (will create a CLAUDE.md for the docops repo itself)
  • Flip TP-018 / TP-020 / TP-021 / TP-022 status from backlog to done

🤖 Generated with Claude Code

nachiket and others added 11 commits April 23, 2026 00:43
Ships the four CLI query commands defined in ADR-0018. Agents can now
retrieve a focused slice of the index without loading the full
.index.json: look up one doc (get), filter/sort the listing (list),
walk typed edges (graph), or get a prioritised task recommendation
(next). Shared bootstrap extracted to bootstrap.go. 27 new tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ships docops search with text match (title > tags > body ranking),
structured filters (--kind/--status/--coverage/--tag/--priority/
--assignee/--since), --regex and --case flags, and --json output.
Also returns root from bootstrapIndex (used by search for lazy body
reads). 20 new tests including a dog-food pass against the live repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rader + update-check

ADR-0023 captures the gstack-shaped update-check pattern: cached probe
of remote VERSION, fail-quiet on network errors, hooked into upgrade so
users learn before syncing stale templates. TP-020 carves out the
internal/scaffold/ refactor as a standalone, behavior-preserving step
so TP-018 (upgrader) only diffs the genuinely new code. TP-021 owns
the update-check implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the helpers TP-018's upgrader will need to share with init into a
new internal/scaffold/ package: Action, the AGENTS.md block markers,
MergeAgentsBlock/ExtractBlock, DirAction/FileAction, Execute, PrintPlan,
and a LoadShippedSkills wrapper around templates.Skills(). initter.go
shrinks from 429 to ~225 lines and re-imports from scaffold; the public
Run/Options/Result surface is unchanged (Action stays available via a
type alias). Tests cover the moved helpers directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New internal/updatecheck/ package implements the gstack-shaped pattern:
cache last-update-check under ~/.docops/ with split TTLs (6h up-to-date,
24h upgrade-available), fetch from raw.githubusercontent.com/.../VERSION
with a 5s timeout, fail quiet on every transient error so docops never
blocks a commit hook. Snooze file with escalating windows (24h/48h/7d)
keeps reminders bounded. Skips entirely on dev builds, +dirty trees,
and DOCOPS_UPDATE_CHECK=off.

Standalone subcommand `docops update-check` prints UP_TO_DATE / UPGRADE_AVAILABLE
on one line (or nothing when skipped) so users and shell hooks can
script around it. --force bypasses cache, --snooze records a snooze
for the current available remote, --json emits structured output.

VERSION file at the repo root (initially 0.1.1) is the upstream source
of truth; release-time bumps land alongside the git tag.

The upgrade integration (warn before in-place sync when binary is
behind) ships with TP-018.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the in-band upgrade path specified by ADR-0021. After
brew/scoop bumps the binary, `docops upgrade` syncs the shipped skills,
schemas, and the AGENTS.md docops block in place — no clobbering of
docops.yaml or the pre-commit hook unless the user opts in via --config
or --hook.

Removed-skill deletion is scoped to the .claude/skills/docops/ and
.cursor/commands/docops/ subdirectories. A .docops-manifest sentinel
written on first successful run lets subsequent upgrades distinguish
"shipped removal" from "user added a custom skill"; the latter trips
a refusal (exit 2) so user files are never silently deleted.

Wires update-check (TP-021) into the upgrade pre-flight: a stale
binary triggers a warning + interactive prompt before any sync runs,
so users do not ship the older templates after forgetting to brew
upgrade. --yes / non-TTY stdin proceed silently.

cmd_upgrade ships --dry-run, --yes/-y, --config, --hook, --json with
the action vocabulary promised by ADR-0021 (new, refreshed, removed,
up-to-date, block-refreshed). scaffold gains KindRemove + Execute
support. New internal/upgrader/ package + tests + cmd-level tests +
templates/skills/docops/upgrade.md + README "Upgrading" subsection +
AGENTS.md.tmpl mention + skills lint update.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Captures the decision to write both CLAUDE.md and AGENTS.md from
docops init/upgrade, sharing the same docops-managed block. Closes
the gap where Claude Code reads CLAUDE.md by default and never sees
the docops invariants. Smaller change than gstack's host-rewrite
approach; no symlink portability risk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both files now carry the same docops block so Claude Code (which reads
CLAUDE.md by default) and other agents (which read AGENTS.md) see the
same project-state invariants. init writes both; upgrade refreshes both
and creates either one if absent — so v0.1.x users gain CLAUDE.md on
first post-ADR-0024 upgrade. Existing planAgents helpers in initter and
upgrader are generalized to planMarkdownBlock(rel, tmpl) and called
twice (once per file).

A new templates-package test asserts byte-identical docops blocks
between AGENTS.md.tmpl and CLAUDE.md.tmpl so future template authors
catch drift at build time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New 'make release VERSION=X.Y.Z' target validates the version, bumps
the VERSION file, commits, tags annotated, and pushes both — replacing
the manual "edit VERSION, commit, tag, push" sequence with one
command. Refuses on a dirty tree, off-main branch, or pre-existing
tag. DRY_RUN=1 prints what would happen without writing.

Release workflow gains a guard step that fails the run if the pushed
tag does not match the VERSION file body. Catches the easy mistake of
tagging manually without bumping VERSION (which would silently break
docops update-check for users on the previous release, since the file
is what raw.githubusercontent.com serves).

README "Release" section updated to describe the new flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
git status --porcelain treats untracked files as dirty; the docops
binary often sits at the repo root after a smoke test and would
spuriously block a release. diff-index --quiet HEAD only reports
modifications to tracked files, which is what we actually need.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nachiket-p nachiket-p merged commit d7163ee into main Apr 23, 2026
7 of 8 checks passed
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.

1 participant