Skip to content

feat: dogfood APM on microsoft/apm -- .apm/ primitives, compile, --check gate#842

Draft
sergio-sisternes-epam wants to merge 10 commits intomicrosoft:mainfrom
sergio-sisternes-epam:feat/792-dogfood-apm
Draft

feat: dogfood APM on microsoft/apm -- .apm/ primitives, compile, --check gate#842
sergio-sisternes-epam wants to merge 10 commits intomicrosoft:mainfrom
sergio-sisternes-epam:feat/792-dogfood-apm

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

Summary

Closes #695, closes #792. Supersedes closed #754.

Dogfoods APM on microsoft/apm itself:

  • The repo's own agent primitives move from hand-maintained .github/instructions/** + .github/agents/** into a .apm/ source tree (9 instructions, 10 agents, 8 skills).
  • A new apm.yml at the repo root (name: apm-cli, target: all) drives compilation.
  • apm compile now regenerates every agent-tool output: root AGENTS.md + CLAUDE.md, distributed AGENTS.md + CLAUDE.md under .github/, docs/src/, src/apm_cli/, src/apm_cli/integration/, tests/, and the new .github/copilot-instructions.md.
  • CI enforces sync via apm compile --check in Tier 1 so hand-edits to generated files or forgotten recompiles fail the build with a clear remediation hint.

What's new

  • feat(compile): .github/copilot-instructions.md emitter (apm compile should emit .github/copilot-instructions.md (dogfood gap) #792). Root-scoped instructions (no applyTo) now compile into a single aggregated file that Copilot reads natively. Root-scoping is a first-class primitive -- the No 'applyTo' warning was a bug masquerading as a lint.
  • feat(compile): apm compile --check flag. Read-only drift verification. Exit codes 0 match / 1 drift / 2 unrecoverable error. Drift report to stderr (stdout reserved for future --json), silent on success. Distinguishes content drift (apm compile) from stale files (apm compile --clean).
  • fix(compilation): link_resolver._resolve_path containment. Three independent gates (is_absolute, validate_path_segments, ensure_path_within) fail closed on traversal, symlink escape, and absolute paths. Supply-chain-security review: 0 blockers.
  • fix(compilation): route timing output through logger and use deterministic base_dir for sort stability across platforms.

How the dogfood works

  1. Edit any primitive under .apm/**.
  2. Run apm compile locally; commit the regenerated outputs in the same PR.
  3. CI Tier 1 runs apm compile --check -- drift fails the build and tells the contributor exactly which command to run.

See CONTRIBUTING.md -- Recompiling agent outputs and the new ## Dogfooding section in the README.

Commit structure

# Commit Summary
1 fbaa469 fix(compilation): route timing output through logger; deterministic sort
2 79389ab fix(compilation): path containment in link_resolver._resolve_path
3 8372876 feat(compile): emit .github/copilot-instructions.md (#792)
4 7f8f95d feat(compile): add --check flag
5 1ce108f chore: root apm.yml + populate .apm/ with repo primitives
6 74b772b chore: regenerate agent-tool outputs via apm compile
7 6d09d4e ci: add apm compile --check gate to Tier 1
8 8f6aded docs: document --check and the .apm/ workflow
9 125c48b docs(readme): add ## Dogfooding section

Each commit is atomic and green.

Validation

  • Full unit suite: 4829 tests passing in ~12s (uv run pytest tests/unit tests/test_console.py).
  • uv run apm compile --check exits 0 on HEAD.
  • Generated file count: 13 (root + 5 distributed AGENTS.md/CLAUDE.md pairs + .github/copilot-instructions.md).
  • .gitattributes marks all generated outputs linguist-generated=true so GitHub diffs collapse them and language stats ignore them.

Reviews run

Before opening this PR the following reviews were run and all findings addressed:

  • python-architect -- pre-impl review of commit 3 (copilot-instructions emitter design).
  • cli-logging-expert -- pre-impl review of commit 4 (--check contract, exit codes, stderr, symbols).
  • supply-chain-security-expert -- post-impl review of commit 2 (containment): 0 blockers; minor nits filed as chore(compile): tighten link_resolver._resolve_path input guards #841.
  • rubber-duck -- gated commit 7 before regen; caught a source-scoping blocker (fixture / template primitives leaking into repo outputs) which we fixed by adding compilation.exclude in apm.yml.
  • code-review -- whole-branch final review: approve, no blockers.

Follow-ups (filed, out of scope here)

Known limitations (documented, not blocking)

  • --check stale detection uses a hardcoded _WELL_KNOWN_OUTPUTS list. Distributed AGENTS.md / CLAUDE.md in subdirs need a TODO(stale-distributed-agents) follow-up once the distributed compiler exposes a "possible outputs" query. Content drift is fully covered; stale-only detection is the gap.
  • verbose_detail in the drift report routes through Rich stdout (pre-existing infra constraint), not stderr. Accepted by cli-logging-expert.

Breaking changes

None for consumers of APM.

For microsoft/apm contributors: .github/instructions/** and .github/agents/** no longer exist as source directories. Edit under .apm/ instead, then apm compile. CONTRIBUTING.md covers the workflow.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Sergio Sisternes and others added 9 commits April 22, 2026 13:05
…istic sort base_dir

Two foundational fixes required before apm compile --check can enforce a strict
stdout contract and round-trip determinism:

1. context_optimizer.py: replace bare print() with logger.debug(). Timing
   output is opt-in via --verbose/DEBUG level and must never leak to default
   stdout.

2. template_builder.py: build_conditional_sections() now takes a required
   base_dir parameter. Previously Path.cwd() was used in the sort key and
   relative-path display, making compile output depend on the user's current
   working directory. Sort order must be deterministic regardless of where
   apm is invoked from.

Updated the single caller in agents_compiler.py to pass self.base_dir. Added
unit tests covering logger routing (caplog vs capsys) and deterministic sort
behaviour.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The apm compile --check flag (landing in a later commit) will run in Tier 1
CI and read user-authored markdown from .apm/ primitives. _resolve_path
previously accepted absolute paths unguarded and performed naive base_path/path
joins with no traversal or symlink-escape checks. This is the first path_security
import into the compilation/ subsystem.

New contract (3-gate fail-closed):
1. Absolute paths are rejected outright (return None).
2. validate_path_segments rejects '..' at parse time; './' is allowed since it
   is legitimate in markdown links (allow_current_dir=True).
3. ensure_path_within resolves symlinks and asserts containment after the join;
   the resolved path is returned on success.

PathTraversalError / OSError / ValueError all map to None, which the caller
surfaces as 'Referenced file not found' via the existing validate_link_targets
flow.

Three other pre-existing path-traversal gaps in the compilation subsystem
(apm.yml output_path, applyTo patterns, full path_security integration across
compile) are explicitly deferred to a labelled security follow-up issue filed
pre-merge.

Supply-chain-security-expert review: 0 blockers, 0 majors, 3 minors (all
non-exploitable edge cases filed under the follow-up).

Tests: TestResolvePathSecurity covers absolute rejection, traversal at depth,
current-directory allowance, symlink escape, and an integration check via
validate_link_targets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…instructions (microsoft#792)

Closes microsoft#792. Instructions with empty or missing applyTo frontmatter
were previously silently dropped by both the distributed compiler and the
single-file template builder. They now aggregate into .github/copilot-instructions.md
for the vscode-family targets (vscode, copilot, agents, opencode, codex, all,
minimal).

Design (approved by python-architect pre-implementation):

- New AgentsCompiler._compile_copilot_instructions() sibling emitter called
  between _compile_agents_md and _compile_claude_md in the target-routing block.
  Returns Optional[CompilationResult]; None when no root-scoped instructions
  exist so no empty file is written and _merge_results stays clean.
- New should_compile_copilot_instructions() predicate in target_detection so
  the gate can diverge from should_compile_agents_md later if needed.
- New build_root_sections() helper in template_builder mirrors
  build_conditional_sections but without pattern headers; filters empty-applyTo
  instructions, deterministic sort by portable_relpath(path, base_dir).
- Hardcoded path .github/copilot-instructions.md (GitHub Copilot convention);
  no new CompilationConfig field needed.
- Prefixed stat key 'copilot_instructions_written' to avoid _merge_results
  collisions.

Also standardises the generated-file header across all three emitters. New
constant GENERATED_HEADER in compilation/constants.py:

Replaces the inconsistent AGENTS.md ('from distributed .apm/ primitives'),
CLAUDE.md ('Generated by APM CLI') and template-builder variants. Updates the
target-description strings in target_detection so apm init / status output
reflects the new file.

Tests: new tests/unit/compilation/test_copilot_instructions.py with 12 cases
covering mixed fixture, empty case, deterministic sort, round-trip stability,
header presence, source attribution, dry-run mode, target gating, and an
integration test asserting both AGENTS.md and copilot-instructions.md are
produced with the correct content split. Existing tests updated for the header
standardisation.

Full suite: 4804 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a CI-friendly --check mode to `apm compile` that compares the
expected compiler output against on-disk generated files and exits:
  0 – all outputs match,  1 – drift or stale files,  2 – unrecoverable.

Implementation highlights:
- New CATEGORY_DRIFT diagnostic level with drift() / drift_count
- AgentsCompiler.preview_all_outputs() dry-runs the full pipeline and
  returns Dict[Path, str] without writing to disk
- _run_check() / _render_drift_report() in the CLI compare previews to
  disk, detect stale well-known outputs, and emit a concise report
- --check implies --local-only and is mutually exclusive with --validate,
  --watch, --dry-run, --single-agents, --clean
- Remove 'No applyTo pattern specified' validation warning – root-scoped
  instructions are now first-class primitives

Co-authored-by: Claude <noreply@anthropic.com>
Creates the first APM manifest for microsoft/apm itself and ports all
26 existing agent primitives from .github/** into .apm/:

  .apm/instructions/ (9)  - 8 copied from .github/instructions/ plus
                            new contributing.instructions.md aggregating
                            .github/copilot-instructions.md as a
                            root-scoped (applyTo-less) instruction.
  .apm/agents/      (10)  - byte-identical copies of .github/agents/.
  .apm/skills/       (8)  - byte-identical copies of .github/skills/.

This commit adds sources only; it does NOT regenerate .github/** outputs.
apm compile --check now reports drift for every .github/** file, which
the next commit resolves by running `apm compile` for real.

Refs microsoft#695, microsoft#792.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes legacy .github/instructions/ and .github/agents/ source trees
(ported to .apm/ in the previous commit) and regenerates all
agent-tool outputs from .apm/ primitives:

- Root AGENTS.md and CLAUDE.md (aggregated)
- Distributed AGENTS.md and CLAUDE.md in .github/, docs/src/,
  src/apm_cli/, src/apm_cli/integration/, tests/
- .github/copilot-instructions.md (root-scoped instructions)

Adds compilation.exclude patterns to apm.yml to scope discovery to
first-party primitives (excludes tests/, templates/, packages/,
build/, docs/node_modules/).

Marks generated outputs as linguist-generated in .gitattributes so
GitHub's diff views collapse them by default and language stats
exclude them.

Removes AGENTS.md from .gitignore since it is now a committed
compile artifact.

Closes microsoft#695, closes microsoft#792.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Runs 'apm compile --check' on every PR and merge_group event to
ensure AGENTS.md, CLAUDE.md, and .github/copilot-instructions.md
stay in sync with .apm/ primitives.

Read-only check: exit 0 when outputs match, exit 1 on drift. The
error message directs contributors to run 'apm compile' locally
and commit the regenerated outputs.

Part of the microsoft#695 / microsoft#792 dogfooding PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- CONTRIBUTING.md: new "Recompiling agent outputs" section explaining
  .apm/ is source of truth and the compile workflow
- docs/.../cli-commands.md: document apm compile --check flag and
  exit-code contract
- docs/.../manifest-schema.md: document compilation.exclude patterns
- CHANGELOG.md: entries under [Unreleased] for --check, copilot-
  instructions compile target, root-scoped instructions, dogfood
  switch, and link_resolver containment fix

README callout proposal written to session files for user approval
(per doc-sync rule 2).

Part of the microsoft#695 / microsoft#792 dogfooding PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM uses APM to manage its own agent primitives. The new section
points readers at the .apm/ source tree, the apm compile workflow,
and the CI gate -- linking to CONTRIBUTING.md for the full procedure.

Part of the microsoft#695 / microsoft#792 dogfooding PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the feat/792-dogfood-apm branch 2 times, most recently from e4d1001 to 1ccc60d Compare April 22, 2026 17:43
Compiled outputs fall into two groups:

- GitHub-hosted consumers (Copilot in PR/chat, Agentic Workflows, Cloud Agents)
  read AGENTS.md and .github/** directly from the repo -- no build step runs
  on their side, so we MUST commit these files.
- Claude Code runs exclusively on a developer's machine. Contributors can
  regenerate CLAUDE.md locally via apm compile.

This commit implements that split:

- .gitignore: gitignore CLAUDE.md / **/CLAUDE.md (with exceptions for template
  fixtures, docs-site pages, and test fixtures).
- Untrack the 5 previously-tracked CLAUDE.md files.
- .gitattributes: drop CLAUDE.md linguist-generated entries (no longer tracked);
  keep AGENTS.md and copilot-instructions.md markers.
- .github/workflows/ci.yml: scope the drift gate to 'apm compile -t copilot
  --check' so CI only asserts sync on the outputs we actually commit.
- CONTRIBUTING.md / README.md / cli-commands.md: document the policy
  explicitly ('pre-built for GitHub Copilot; other platforms run apm compile
  on checkout') including a note on how local -t copilot --check interacts
  with a full-target local compile.
- CHANGELOG.md: clarify the Unreleased dogfood entry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant