Skip to content

feat: Auth + Logging Architecture Overhaul (#393)#394

Open
danielmeppiel wants to merge 37 commits intomainfrom
feat/auth-logging-architecture-393
Open

feat: Auth + Logging Architecture Overhaul (#393)#394
danielmeppiel wants to merge 37 commits intomainfrom
feat/auth-logging-architecture-393

Conversation

@danielmeppiel
Copy link
Collaborator

@danielmeppiel danielmeppiel commented Mar 21, 2026

Auth + Logging Architecture Overhaul

Closes #393 and #327. Subsumes #389 (credential timeout fix — credit to @frblondin).

Problem

  1. Auth was scattered: 4 different call sites reimplemented token lookup. EMU/enterprise packages silently failed validation because git ls-remote ran without auth.
  2. Logging was ad-hoc: 766+ output calls across 27 files with no shared lifecycle. Misleading counts, contradictory symbols, MCP noise on partial install.
  3. Global env var host-gating: Global tokens were restricted to the "default host" — framed as a security constraint but HTTPS is the real transport boundary. This forced users into per-org token setups unnecessarily.

Solution

AuthResolver (src/apm_cli/core/auth.py)

  • Single source of truth for auth: per-(host, org) resolution, cached, thread-safe
  • Resolution chain: per-org env var → global env vars → git credential fill
  • Global env vars apply to all hosts (no host-gating). When a token doesn't work for a particular host, try_with_fallback() retries with git credential fill automatically
  • try_with_fallback(): unauth-first for validation (saves rate limits), auth-first for downloads, credential-fill fallback on failure

CommandLogger (src/apm_cli/core/command_logger.py)

  • Base class for all CLI command output — delegates to _rich_* helpers
  • InstallLogger subclass with validation/resolution/download phases
  • Short-circuits on total validation failure (no confusing "0 dependencies" messages)
  • All 13 commands wired to use logger

Auth Flow

flowchart TD
    A[Dependency Reference] --> B{Per-org env var?}
    B -->|GITHUB_APM_PAT_ORG| C[Use per-org token]
    B -->|Not set| D{Global env var?}
    D -->|GITHUB_APM_PAT / GITHUB_TOKEN / GH_TOKEN| E[Use global token]
    D -->|Not set| F{Git credential fill?}
    F -->|Found| G[Use credential]
    F -->|Not found| H[No token]

    E --> I{try_with_fallback}
    C --> I
    G --> I
    H --> I

    I -->|Token works| J[Success]
    I -->|Token fails| K{Credential-fill fallback}
    K -->|Found credential| J
    K -->|No credential| L{Host has public repos?}
    L -->|Yes| M[Try unauthenticated]
    L -->|No| N[Auth error with actionable message]
Loading

Provenance Matrix

Package source Host Auth behavior Fallback
org/repo (bare) default_host() Global env vars → credential fill Unauth for public repos
github.com/org/repo github.com Global env vars → credential fill Unauth for public repos
contoso.ghe.com/org/repo *.ghe.com Global env vars → credential fill fallback Auth-only (no public repos)
GHES via GITHUB_HOST ghes.company.com Global env vars → credential fill Unauth for public repos
dev.azure.com/org/proj/repo ADO ADO_APM_PAT only Auth-only
Multi-org on any host Per-org GITHUB_APM_PAT_{ORG} → global → credential fill Per host type

Token Prefix Classification

Prefix Type Origin
github_pat_ Fine-grained PAT github.com settings
ghp_ Classic PAT github.com settings
ghu_ OAuth user-to-server gh auth login, OAuth apps
gho_ OAuth app GitHub OAuth apps
ghs_ GitHub App installation Server-to-server
ghr_ GitHub App refresh Token refresh

EMU note: Enterprise Managed Users use standard PAT prefixes (ghp_, github_pat_). There is no EMU-specific prefix — APM cannot detect EMU from a token.

Key Design Decisions

  1. HTTPS is the security boundary — all token-bearing requests use HTTPS. No host-gating needed.
  2. Credential-fill fallback — when a global env-var token fails (e.g., github.com PAT on *.ghe.com), try_with_fallback() retries with git credential fill. This makes gh auth login for multiple hosts work seamlessly.
  3. Per-org tokens take highest priorityGITHUB_APM_PAT_{ORG} always wins regardless of host.
  4. Credential timeout: 5s → 60s (configurable via APM_GIT_CREDENTIAL_TIMEOUT, max 180s). Fixes Windows credential picker timeouts.

Commits

Commit Description
6366f9b AuthResolver + CommandLogger foundation with full auth wiring
fdc7d08 InstallLogger wiring, auth docs, CHANGELOG, test updates
6ef2511 CommandLogger wiring across all commands and support modules
c355d02 EMU docs accuracy, credential timeout (subsumes #389)
6ddaea4 Correct token prefix mapping — EMU uses standard PAT prefixes
37978ac Remove host-gating on global env vars, add credential-fill fallback

Test Coverage

  • 2851 tests passing (up from 2829 on main), 127 skipped, 0 failures
  • 35 unit tests for AuthResolver (classify_host, detect_token_type, resolve, try_with_fallback, credential fallback)
  • 44 unit tests for CommandLogger
  • 27 integration tests for AuthResolver (end-to-end with env manipulation)
  • 11 diagnostics tests

danielmeppiel and others added 3 commits March 20, 2026 23:55
Phase 1 — Foundation:
- AuthResolver: per-(host,org) token resolution, host classification,
  try_with_fallback(), build_error_context(), detect_token_type()
- CommandLogger base + InstallLogger subclass with semantic lifecycle
- DiagnosticCollector: CATEGORY_AUTH, auth() method, render group

Phase 2 — Auth wiring (all touchpoints):
- github_downloader.py: AuthResolver injected, per-dep token resolution
  in _clone_with_fallback and _download_github_file
- install.py: _validate_package_exists uses try_with_fallback(unauth_first=True)
- copilot.py + operations.py: replaced os.getenv bypasses with TokenManager
- All 7 hardcoded auth error messages replaced with build_error_context()

Security fix: global env vars (GITHUB_APM_PAT) no longer leak to
non-default hosts (GHES, GHE Cloud, generic). Enterprise hosts resolve
via per-org env vars or git credential helpers only.

Phase 4 — Tests:
- test_auth.py: 34 tests (classify_host, resolve, try_with_fallback,
  build_error_context, detect_token_type)
- test_command_logger.py: 44 tests (CommandLogger, InstallLogger,
  _ValidationOutcome)
- Fixed test_github_downloader_token_precedence.py import paths
- Updated test_auth_scoping.py for AuthResolver integration

Phase 5 — Agents & Skills:
- 3 agent personas: auth-expert, python-architect, cli-logging-expert
- 2 new skills: auth, python-architecture
- Updated cli-logging-ux skill with CommandLogger awareness

All 2829 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Install UX overhaul (3a):
- Wire InstallLogger into install.py with semantic lifecycle methods
- Short-circuit on total validation failure (no more misleading counts)
- Context-aware messages: partial vs full, new vs locked dependencies
- Demote noise (lockfile info, MCP transitive, orphan cleanup) to verbose

Documentation (5a, 5b):
- Rewrite authentication.md: resolution chain, token lookup table,
  per-org setup, EMU/GHE Cloud/GHES sections, troubleshooting
- CHANGELOG entry under [Unreleased] for #393

Test updates (4c):
- 11 new CATEGORY_AUTH tests in test_diagnostics.py
- Fix tuple unpacking in test_canonicalization, test_dev_dependencies,
  test_generic_git_url_install (validation now returns outcome)
- Update error message assertion in test_install_command

All 2839 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 3 — Logging migration (3b-3g):
- compile: CommandLogger in cli.py, _log() helper in agents_compiler.py
- deps: CommandLogger in cli.py + _utils.py (logger=None backward compat)
- audit: CommandLogger, pass logger to helpers, verbose_detail for scan stats
- mcp/run/init: CommandLogger with --verbose support
- config/list/update/pack/runtime/prune/uninstall: CommandLogger wiring
- Support modules: logger=None param in skill/prompt/instruction/mcp
  integrators, vscode adapter, drift module

Phase 4 — Integration tests (4d, 4e):
- test_auth_resolver.py: 26 integration tests across 8 test classes
- Scripts verified — no auth error pattern updates needed

All 2839 tests pass, 126 skipped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel added the enhancement New feature or request label Mar 21, 2026
Copilot AI review requested due to automatic review settings March 21, 2026 00:07
@danielmeppiel danielmeppiel added the enhancement New feature or request label Mar 21, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces two cross-cutting foundations—AuthResolver (per-(host, org) auth resolution) and CommandLogger / InstallLogger (structured CLI lifecycle logging)—and wires them through CLI commands and auth touchpoints to fix EMU/private repo installs, reduce confusing output, and prevent token leakage across hosts.

Changes:

  • Add AuthResolver + host classification + fallback strategies; integrate into clone/download/validation paths.
  • Add CommandLogger/InstallLogger and thread diagnostics through commands and supporting modules.
  • Expand diagnostics with CATEGORY_AUTH, update docs/changelog, and adjust/add unit & integration tests.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/unit/test_unpacker.py Updates dry-run output assertion to match new logging wording.
tests/unit/test_install_command.py Updates invalid-format assertion to match updated validation messaging.
tests/unit/test_diagnostics.py Adds coverage for new CATEGORY_AUTH diagnostics group rendering/counting.
tests/unit/test_dev_dependencies.py Adapts to new (validated, outcome) return from validation helper.
tests/unit/test_command_logger.py New unit tests for CommandLogger, InstallLogger, _ValidationOutcome.
tests/unit/test_canonicalization.py Updates expectations for new validation helper return type.
tests/unit/test_auth_scoping.py Adjusts scoping tests to align with per-dep auth resolution & cache.
tests/unit/test_auth.py New unit tests for AuthResolver (classification, caching, fallback, errors).
tests/unit/test_audit_command.py Updates audit helper calls to pass CommandLogger.
tests/test_token_manager.py Adds tests for configurable credential-helper timeout behavior.
tests/test_github_downloader_token_precedence.py Fixes test imports to use installed package paths.
tests/integration/test_generic_git_url_install.py Updates expectations for new validation helper return type.
tests/integration/test_auth_resolver.py New integration tests gated by APM_E2E_TESTS.
src/apm_cli/utils/diagnostics.py Adds CATEGORY_AUTH + rendering and count helpers.
src/apm_cli/registry/operations.py Uses token manager precedence when auto-filling env vars in CI/E2E mode.
src/apm_cli/integration/skill_integrator.py Threads optional logger into skill integration warnings/diagnostics.
src/apm_cli/integration/prompt_integrator.py Adds optional logger parameter for structured output compatibility.
src/apm_cli/integration/mcp_integrator.py Threads optional logger through MCP install/remove flows; renames module logger to _log.
src/apm_cli/integration/instruction_integrator.py Adds optional logger parameter for structured output compatibility.
src/apm_cli/drift.py Threads optional logger through drift helpers.
src/apm_cli/deps/github_downloader.py Switches to AuthResolver and per-dependency token resolution for clone/download/error context.
src/apm_cli/core/token_manager.py Makes git credential fill timeout configurable; default 60s, max 180s.
src/apm_cli/core/command_logger.py New structured logging base (CommandLogger) and install-specific logger (InstallLogger).
src/apm_cli/core/auth.py New AuthResolver/AuthContext/HostInfo with caching + fallback strategies.
src/apm_cli/core/init.py Exposes auth types from apm_cli.core.
src/apm_cli/compilation/agents_compiler.py Adds optional logger plumbing for compilation output instead of raw print().
src/apm_cli/commands/update.py Migrates update command output to CommandLogger.
src/apm_cli/commands/uninstall/cli.py Migrates uninstall command output to CommandLogger.
src/apm_cli/commands/runtime.py Migrates runtime command output to CommandLogger.
src/apm_cli/commands/run.py Adds --verbose and migrates output to CommandLogger.
src/apm_cli/commands/prune.py Migrates prune command output to CommandLogger.
src/apm_cli/commands/pack.py Migrates pack/unpack output to CommandLogger (incl. dry-run messaging).
src/apm_cli/commands/mcp.py Adds --verbose and migrates MCP commands output to CommandLogger.
src/apm_cli/commands/list_cmd.py Migrates list command output to CommandLogger.
src/apm_cli/commands/install.py Integrates InstallLogger, introduces validation outcome tracking, and auth-aware validation (try_with_fallback).
src/apm_cli/commands/init.py Adds --verbose and migrates init output to CommandLogger.
src/apm_cli/commands/deps/cli.py Migrates deps subcommands output to CommandLogger.
src/apm_cli/commands/deps/_utils.py Threads optional logger into deps update helpers and removes direct console helper usage.
src/apm_cli/commands/config.py Migrates config command output to CommandLogger.
src/apm_cli/commands/compile/cli.py Migrates compile output to CommandLogger and passes logger into compiler.
src/apm_cli/commands/audit.py Migrates audit output to CommandLogger; threads logger into helpers.
src/apm_cli/adapters/client/vscode.py Threads optional logger through VS Code MCP config operations.
src/apm_cli/adapters/client/copilot.py Uses token manager for Copilot header token selection.
docs/src/content/docs/guides/private-packages.md Updates links to auth + git reference docs.
docs/src/content/docs/getting-started/authentication.md Rewrites auth docs to describe per-(host, org) resolution and security constraint.
CHANGELOG.md Adds Keep-a-Changelog entry for new auth/logging architecture and timeout fix.
.github/skills/python-architecture/SKILL.md Adds skill trigger guidance for architecture changes.
.github/skills/cli-logging-ux/SKILL.md Updates logging UX skill to include CommandLogger rules.
.github/skills/auth/SKILL.md Adds skill trigger guidance and “AuthResolver-only” rule.
.github/agents/python-architect.agent.md Adds agent persona for architecture review.
.github/agents/cli-logging-expert.agent.md Adds agent persona for CLI logging UX review.
.github/agents/auth-expert.agent.md Adds agent persona for authentication review.

danielmeppiel and others added 2 commits March 21, 2026 01:18
- Fix docs conflating EMU with *.ghe.com — EMU orgs can exist on
  github.com too; *.ghe.com is specifically GHE Cloud Data Residency
- Increase git credential fill timeout: 5s → 60s default (configurable
  via APM_GIT_CREDENTIAL_TIMEOUT, max 180s) — fixes silent auth failures
  on Windows when credential helper shows interactive account picker
- Add 7 timeout tests
- CHANGELOG entry for credential timeout fix

Credit: credential timeout fix based on investigation by @frblondin in #389.

Co-authored-by: Thomas Caudal <thomas@caudal.fr>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ghu_ is OAuth user-to-server (e.g. gh auth login), NOT EMU.
EMU users get regular ghp_ (classic) or github_pat_ (fine-grained)
tokens — there is no prefix that identifies EMU. EMU is a property
of the account, not the token format.

Changes:
- detect_token_type: ghu_ → 'oauth', gho_ → 'oauth',
  ghs_/ghr_ → 'github-app' (was all 'classic')
- build_error_context: replace token_type=='emu' check with
  host-based heuristics (*.ghe.com → enterprise msg, github.com →
  SAML/EMU mention)
- Auth docs: remove ghu_ as EMU prefix, clarify EMU uses standard PATs
- Agent persona: correct token prefix reference table
- Tests: update detect_token_type + build_error_context assertions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel force-pushed the feat/auth-logging-architecture-393 branch from 0e12ae5 to 6ddaea4 Compare March 21, 2026 00:18
danielmeppiel and others added 3 commits March 21, 2026 01:45
Global env vars (GITHUB_APM_PAT, GITHUB_TOKEN, GH_TOKEN) now apply to
all hosts — not just the default host. HTTPS is the real transport
security boundary; host-gating was unnecessary and forced users into
awkward per-org token setups.

When a global token doesn't work for a particular host (e.g. github.com
PAT on *.ghe.com), try_with_fallback() retries with git credential fill
before giving up. This preserves seamless behavior for gh auth login
users with multiple hosts.

Changes:
- auth.py: remove _is_default gate in _resolve_token(), add
  _try_credential_fallback() in try_with_fallback()
- authentication.md: remove Security constraint section, simplify token
  table, fix EMU example, add HTTPS note
- 5 new tests, 3 updated tests (2851 passing)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
resolution_start() was using len(packages) (user-supplied arguments)
instead of len(validated_packages) (packages that passed validation).
This caused misleading 'Installing N new packages' when some packages
failed validation or were already in apm.yml.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 52 out of 52 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/apm_cli/core/command_logger.py:174

  • InstallLogger.validation_pass()/validation_fail() embed Unicode check/cross characters directly in the output strings. Since the console helpers already support ASCII-safe symbols via symbol=... (see STATUS_SYMBOLS), consider switching these to use symbol instead of inline glyphs to keep output consistent and Windows-friendly.
    def validation_pass(self, canonical: str, already_present: bool):
        """Log a package that passed validation."""
        if already_present:
            _rich_echo(f"  ✓ {canonical} (already in apm.yml)", color="dim")
        else:
            _rich_success(f"  ✓ {canonical}")

    def validation_fail(self, package: str, reason: str):
        """Log a package that failed validation."""
        _rich_error(f"  ✗ {package} — {reason}")

danielmeppiel and others added 7 commits March 21, 2026 07:33
_validate_package_exists was referencing _rich_echo which was not
imported, causing a silent NameError caught by the outer try/except.
Added _rich_echo import and wired verbose_callback through both
try_with_fallback call sites so --verbose shows the full auth
resolution chain during package validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verbose output now shows:
- Auth resolution summary (host, org, token source, token type)
- Sanitized git stderr on each failed attempt (no token leaked)
- Full fallback chain visibility: unauth → PAT → credential fill

This gives users actionable diagnostics when auth fails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fine-grained PATs (github_pat_) get 403 when using the
x-access-token:{token}@host URL format because GitHub rejects Basic
auth for these tokens. Switch validation to use:

  git -c http.extraHeader='Authorization: Bearer {token}' ls-remote

This works with ALL token types (fine-grained, classic, OAuth, GitHub
App). The x-access-token URL format only works reliably with classic
PATs (ghp_) and GitHub App installation tokens (ghs_).

Note: the broader clone path (_build_repo_url / build_https_clone_url)
still uses x-access-token — tracked for a follow-up fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
git ls-remote with http.extraHeader has a fatal flaw: the Bearer
header persists across credential-helper retries, preventing git from
using credentials from 'gh auth' or OS keychain when the env-var
token fails.

Switch to GitHub REST API (GET /repos/{owner}/{repo}) which:
- Works with ALL token types (fine-grained, classic, OAuth, App)
- Returns clean HTTP status codes (200=ok, 404=no access, 401=bad token)
- No credential-helper interference
- Faster than spawning git subprocess

The git ls-remote approach remains in the ADO/GHES path where the
API endpoint may differ.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fine-grained PATs are scoped to a single resource owner (user or org).
A user-scoped PAT cannot access org repos — even for internal repos
where the user is a member. Document required permissions (Metadata
Read + Contents Read), resource owner setup, and alternatives.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Wire build_error_context() into validation failure path so verbose
  mode shows actionable auth guidance (env vars, token setup)
- Add '--verbose for auth details' hint when validation fails without
  verbose mode
- Add ADO guard to _try_credential_fallback() preventing credential
  fill for Azure DevOps hosts (consistent with _resolve_token policy)
- Add verbose logging to git ls-remote validation path (ADO/GHE/GHES)
  matching the GitHub API path's diagnostic output
- Add '--verbose for detailed diagnostics' hint to top-level install
  error handlers
- Fix traffic-light violations: orphan removal failure → error (was
  warning), 'no new packages' → info (was warning), hash mismatch
  re-download → info/progress (was warning, auto-recovery)
- Add 3 new tests for validation failure reason messages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danielmeppiel and others added 5 commits March 21, 2026 11:06
Auth acceptance:
- scripts/test-auth-acceptance.sh: 10 P0 auth scenarios covering
  public/private repos, token priority, fallback chain, error paths.
  Runnable locally (set tokens + run) or via CI workflow_dispatch.
- .github/workflows/auth-acceptance.yml: manual trigger with inputs
  for test repos, uses auth-acceptance environment for secrets.

Logging acceptance:
- tests/acceptance/test_logging_acceptance.py: 16 tests verifying
  install output contract (validation, errors, dry-run, verbose,
  symbol consistency, diagnostics ordering) — fully mocked, no
  network needed.

Moderate UX fixes:
- Route hash mismatch, orphan removal, and lockfile errors through
  DiagnosticCollector for deferred summary rendering
- Fix compile/cli.py color bypasses: _rich_echo(color=yellow/red)
  replaced with logger.warning()/logger.error()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add scenarios for: GH_TOKEN fallback, EMU internal repos, credential
helper only (gh auth), token type detection, mixed manifest (public +
private), ADO with/without PAT, fine-grained PAT wrong resource owner.

Update workflow with GHE/ADO inputs and secrets. Fix emoji symbols
to ASCII-safe [+]/[x]/[-] matching CLI convention.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Complete rewrite with:
- Top-level scenario matrix documenting all 18 scenarios across 4
  dimensions (sources A1-A7, hosts H1/H2/H4, repos V1-V3, tokens T1-T5)
- Per-function docblock explaining the auth dimension, expected behavior,
  and WHY the assertion matters
- Full local usage guide covering ALL env vars: GITHUB_APM_PAT,
  GITHUB_APM_PAT_{ORG}, GITHUB_TOKEN, GH_TOKEN, ADO_APM_PAT
- All repo types: public, private, EMU internal, ADO
- ASCII-safe symbols matching CLI conventions
- Auto-skip with clear reason when required env vars are missing
- Startup banner showing which tokens and repos are configured

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use tee + PIPESTATUS to stream APM install output to stdout in
real-time while capturing it for assertions. Enables live debugging
in CI/CD environments — no need to wait for scenario completion to
see what happened.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add two new E2E auth scenarios that test the hardest real-world case:
a single apm.yml mixing dependencies from multiple auth domains.

Scenario 19 (mega-manifest): builds one manifest with all configured
repos (public, private, EMU, ADO, second-org) and verifies every dep
installs when all tokens are set simultaneously.

Scenario 20 (multi-org per-org PAT routing): two private repos from
different orgs with ONLY per-org PATs — no global GITHUB_APM_PAT.
Validates the resolver routes each dep to its own per-org token.

Also fixes:
- Replace bash 3.2-incompatible `declare -A` with indexed arrays
  (macOS portability)
- Fix `set -e` bug in run_install that enabled errexit and caused
  the script to exit on first assertion failure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrite scenario 19 as a truly brutal chaos mega-manifest that mixes
every dependency format and auth source in a single apm.yml install:
public shorthand, public FQDN, private org A, private org B, EMU
internal, and ADO — all in one pass. The resolver must route each dep
to its correct token independently.

Add --mega flag to run ONLY the chaos manifest (vs progressive mode
which runs all 20 scenarios). Usage:

  set -a && source .env && set +a
  ./scripts/test-auth-acceptance.sh           # progressive (all 20)
  ./scripts/test-auth-acceptance.sh --mega    # chaos mega only

Create .env (gitignored) with documented token placeholders and usage
instructions for running the test suite locally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel added bug Something isn't working priority/high Ships in current or next milestone labels Mar 21, 2026
danielmeppiel and others added 2 commits March 21, 2026 12:55
Add slots 7 and 8 to the mega-manifest for YAML dict format deps:
- Slot 7: private repo via { git: https://...git } (AUTH_TEST_GIT_URL_REPO)
- Slot 8: public repo via { git: https://...git } (AUTH_TEST_GIT_URL_PUBLIC_REPO)

These test parse_from_dict() — a completely different parser path than
string shorthand or FQDN. Auth resolves from the URL's host+org.

Both slots use separate env vars to avoid unique-key dedup with the
string shorthand slots. Slot 7 falls back to PRIVATE_REPO_2 if unset.

Also updates .env template with the new repo vars.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a transitive dependency fails to download (e.g., auth failure for
an unknown org), error messages now include the full dependency chain
that led to the failure:

  Failed to resolve transitive dep other-org/leaf-pkg
    (via acme/root-pkg > other-org/leaf-pkg): 401 Unauthorized

Architecture (clean SoC):
- Resolver (_build_parent_chain): computes breadcrumb by walking up
  DependencyNode.parent links — it owns the tree structure
- Callback (install.py): receives chain as optional 3rd arg, uses it
  for verbose logging and diagnostics — it owns the output
- DownloadCallback type: changed to Callable[...] to accept the new
  parent_chain parameter without breaking existing signatures

Transitive failures are now:
1. Logged inline via logger.verbose_detail() (verbose mode)
2. Collected into DiagnosticCollector for the deferred summary (always)

Tests:
- 3 unit tests for _build_parent_chain (3-level, single, None)
- 1 integration test: resolver with callback tracking verifies the
  chain string contains parent dep name and '>' separator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danielmeppiel and others added 8 commits March 21, 2026 13:52
…dedup auth, traffic-light fixes

- Replace Callable[...] DownloadCallback with typed Protocol class
- Move _build_parent_chain() to DependencyNode.get_ancestor_chain()
- Remove unused _resolution_path field from resolver
- Deduplicate AuthResolver instantiation in _validate_package_exists
- Fix traffic-light: 'no deps' warning→info, lockfile failure warning→error
- All 2874 tests passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ebase

Wave 1 (verbose coverage):
- Add dep tree resolution summary with transitive breadcrumbs
- Add auth source/type logging in download phase (verbose only)
- Add manifest parsing + per-dep lockfile SHA in verbose output
- Add download URL verbose logging via verbose_callback in downloader

Wave 2 (CommandLogger migration):
- compile/watcher.py: 19 _rich_* calls → CommandLogger
- uninstall/engine.py: 15 _rich_* calls → CommandLogger, remove unicode symbols
- safe_installer.py: 6 calls with logger fallback pattern
- _helpers.py, packer.py, plugin_exporter.py: logger threading
- audit.py already migrated (verified)

All 2874 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
install.py:
- Remove 30 dead else-branch _rich_* fallbacks (logger always available)
- Remove 3 duplicate _rich_* calls alongside logger calls
- Remove unused _rich_info import; clean test mocks

MCPIntegrator:
- Add diagnostics param to collect_transitive() and install()
- Thread diagnostics from install.py; transitive trust warning uses diagnostics

skill_integrator: normalization warning already routes through diagnostics ✅

All 2874 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- command_logger.py: ✓→[+], ✗→[x], —→--
- diagnostics.py: ⚠→[!], ✗→[x], —→--
- install.py: ✓→[+], →→->, —→--
- Updated test assertions to match new ASCII symbols

All 2874 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comprehensive handbook for AI Engineers using GitHub Copilot CLI
to ship large multi-concern changes via agent fleets.

- Three-actor model: User (strategist), Harness (Copilot CLI), Agents (fleet)
- Full meta-process: Audit, Plan, Wave Execution, Validate, Ship
- Repository instrumentation guide (agents, skills, instructions)
- Wave execution model with dependency mapping and parallelization
- Test ring pipeline (unit, acceptance, integration)
- Escalation protocol and feedback loop for primitive improvement
- Autonomous CI/CD patterns for drift detection
- Live Dashboard POC via Copilot CLI Hooks (Appendix D)
- Prompt examples written from the user's perspective

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…formats

- Pipe < /dev/null to apm install in run_install/run_install_manifest to
  prevent MCP env prompts from blocking in non-interactive test runs
- Update mega test assertion to match both validation-phase (source=X)
  and install-phase (Auth: X) verbose formats

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add mode input (all/mega) to select test mode
- Add missing inputs: private_repo_2, git_url_repo, git_url_public_repo
- Wire per-org PAT secrets: MCAPS_MICROSOFT, DEVEXPGBB
- ADO repo description now shows FQDN format requirement

Secrets to configure in 'auth-acceptance' environment:
  AUTH_TEST_GITHUB_APM_PAT, AUTH_TEST_GITHUB_TOKEN, AUTH_TEST_GH_TOKEN,
  AUTH_TEST_ADO_APM_PAT, AUTH_TEST_GITHUB_APM_PAT_MCAPS_MICROSOFT,
  AUTH_TEST_GITHUB_APM_PAT_DEVEXPGBB

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use exact equality for host assertion and endswith() for operation
instead of 'in' substring check that CodeQL flagged as incomplete
URL sanitization (false positive in test code, but clean fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 66 out of 67 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

src/apm_cli/commands/compile/watcher.py:76

  • These log lines include manual status markers like " [x] ...", but CommandLogger.error() already prefixes messages with the [x] symbol via _rich_error(..., symbol="error"). This will result in duplicated symbols in output (e.g., [x] [x] ...). Remove the inline [x]/[!] prefixes and rely on the logger’s symbol= handling (or switch to verbose_detail() for indented sub-lines).
                        self.logger.error("Recompilation failed")
                        for error in result.errors:
                            self.logger.error(f"  [x] {error}")

danielmeppiel and others added 5 commits March 21, 2026 23:39
…rm patterns

Clarifies the GITHUB_ prefix secret naming workaround, adds multi-org
(GITHUB_APM_PAT_{ORG}) and ADO examples, links to auth guide.
Related: microsoft/apm-action#18

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- auth.py: normalize cache key (host.lower(), org.lower()) to prevent
  case-sensitive duplication; gate per-org GITHUB_APM_PAT_{ORG} to
  GitHub-like hosts only (not ADO)
- command_logger.py: replace inline Unicode glyphs with symbol= param;
  remove leading spaces from validation messages (symbol prefix handles it)
- dependency_graph.py: use Optional[] instead of '|' union syntax
- watcher.py: pass logger to compile(); remove inline [x] prefixes
- token_manager.py: platform-aware GIT_ASKPASS (empty on Unix, echo on Win)
- safe_installer.py: remove inline status prefixes when using logger
- operations.py: map token var names to appropriate purposes (ado, copilot)
- apm_resolver.py: fix DownloadCallback docstring to match actual behavior
- ci-cd.md: trim private deps section, link to auth guide instead of bloat

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…k verification failure

When packing with --target vscode, the bundle only contains .github/ files
but the lockfile still listed .claude/ deployed_files. Unpack verification
then failed because those files were 'missing from the bundle'.

Fix: enrich_lockfile_for_pack() now filters each dependency's deployed_files
to match the pack target. Moved _TARGET_PREFIXES and _filter_files_by_target
to lockfile_enrichment (single source of truth), packer imports from there.

Fixes microsoft/apm-action test-restore-artifact CI failure with APM 0.8.3.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`existing_lockfile.dependencies` is a Dict[str, LockedDependency].
Iterating it yields string keys, not LockedDependency objects.
Use `get_all_dependencies()` to iterate values.

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

bug Something isn't working enhancement New feature or request priority/high Ships in current or next milestone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auth + Logging Architecture Overhaul — Complete Wiring

2 participants