Repository navigation
fix(audit): bound the deploy-tree hidden-Unicode walk to primitive directories - #2962
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Fix the target-scope resolution issue and clarify the lockfile union in both documentation pages.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR bounds audit scans to primitive deployment directories, avoiding unrelated session data while preserving lockfile-scoped coverage.
Changes:
- Adds bounded governance partitioning.
- Restricts deployed-tree walks and adds regression tests.
- Documents the updated scan boundaries.
File summaries
| File | Summary |
|---|---|
tests/unit/test_security_file_scanner.py |
Tests bounded scanning and transcript exclusion. |
tests/unit/install/phases/test_lockfile_union.py |
Tests bounded governance partitioning and lockfile coverage. |
src/apm_cli/security/file_scanner.py |
Restricts deployed-tree walks. Critical (2 votes): Uses project-scope resolution instead of supplied scope-resolved targets, potentially omitting global .copilot files. |
src/apm_cli/install/manifest_reconcile.py |
Adds bounded governance partitioning. |
docs/src/content/docs/reference/baseline-checks.md |
Documents scan scope. Nit (1 vote): Explicitly document the lockfile-scoped union. |
docs/src/content/docs/enterprise/security.md |
Documents enterprise scan boundaries. Nit (1 vote): Explicitly document the lockfile-scoped union. |
Review details
Suppressed comments (2)
docs/src/content/docs/enterprise/security.md:200
- This new boundary description omits an important part of the implemented scope: the lockfile-scoped pass is still unioned with the bounded walk, so recorded paths outside primitive directories (for example
.claude/settings.jsonand.claude/apm-hooks.json) are still checked for hidden Unicode. As written, readers could interpret the primitive-directory list as the complete content-integrity scope; please state the lockfile union explicitly.
A whole-project scan checks **every regular file under the deploy directories your targets govern** (primitive directories such as `.claude/skills/` and generated files, not the whole target root) for hidden Unicode, not only files recorded in `apm.lock.yaml`. Hash verification and positional `PACKAGE` scans remain lockfile-scoped because they need recorded ownership. Source content under `.apm/` is not added by the deploy-tree walk; install-time scanning owns that surface, while any `.apm/` path already recorded in the lockfile remains covered.
docs/src/content/docs/reference/baseline-checks.md:134
- The reference page similarly describes the primitive-directory walk but does not say that the lockfile-scoped pass is unioned with it. That distinction matters for the documented examples such as a claimed
.claude/settings.json: it remains hidden-Unicode scanned even though it is outside a primitive directory. Please make that union explicit rather than implying that only primitive-directory files are covered.
1. Critical hidden Unicode (tag characters, bidi overrides, variation selectors 17-256, and similar steganographic markers), over **every file under the deploy directories the project's targets govern** (each target's primitive directories and generated files, such as `.claude/skills/` and `.claude/hooks/`, not the whole `.claude/` root). This signal needs no recorded baseline, so it is deliberately not limited to `deployed_files` -- otherwise a deployed file the lockfile omits would be exempt from scanning for as long as it stayed unrecorded. The rest of a target root (session transcripts, history, caches) is never deployed by APM and is not walked.
- Files reviewed: 6/6 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
1cfdd21 to
3eac2c3
Compare
|
At Run: Drift still catches U+202E in APM's own hook entry and in If dropping user-owned content is the intent (as the PR does with transcripts), only the two doc sentences need to change. If not, adding |
PR triage recommendationneeds-issue Recommendation only, not merge or scope approval. A responsible Linked issue#2961 (open; type/bug, area/cli, area/audit-policy, triage/recommended). Proposed classificationtype/bug, area/cli, area/audit-policy No existing human classification on this PR. Linked issue already carries Suggested next actionWait for a maintainer to accept #2961 ( Thank you for contributing this pull request and for filing #2961 first. APM starts with an issue, not an implementation CODEOWNERS review requests already present: danielmeppiel, This is advisory classification only, not merge or scope approval. Generated by autopilot-pr-triage-worker. This comment is AI-generated and may contain errors. |
…rectories install_governance() returns the whole root for dedicated targets, which is right for ownership but wrong for a tree walk: at user scope ~/.claude and ~/.codex are mostly session transcripts APM never deploys. Scanning them cost minutes of CPU per `apm audit --ci` and produced critical content-integrity findings the audit cannot remediate. Add a `bounded` keyword that partitions every root the way `.agents/` already is, and use it from _scan_deployed_trees. Ownership and prune callers are unchanged. Lockfile-claimed paths outside primitive directories stay covered by the lockfile-scoped scan. _scan_deployed_trees also walks the scope-resolved targets the caller supplies rather than re-resolving at project scope, so a global audit's user roots such as ~/.copilot are no longer skipped. Fixes microsoft#2961 Claude-Session: https://claude.ai/code/session_01Nf7EcPHsefXRkPgvv3dAW3
05b264d to
8f70a29
Compare
|
lkshrk (@lkshrk), thanks for making the settings.json boundary an explicit maintainer decision. The approved scope is now recorded on #2961: The decision is neither "scan the entire settings file for hidden Unicode" nor "drop shared hook configuration from audit." Separate primitive discovery from the checks applicable to each surface.
Please revise this PR against that contract before technical panel review:
The current directory-bounding change is a useful foundation, but this approved contract is more than the earlier two-sentence documentation alternative. Please keep the work in this existing PR, link the approval in its scope section, and state any remaining gaps rather than marking the issue complete before the contract is met. This request does not authorize executing hooks, guessing runtime output, or introducing automatic edits/deletions of user configuration. Review contact: Daniel Meppiel (@danielmeppiel). Scope acceptance does not certify the current implementation, test results, or merge readiness; technical review will follow the revision. |
…-merge-drive-head
Apply the approved scope for microsoft#2961: inventory native hooks without executing commands, inspect documented prompt fields, fail incomplete coverage, and preserve configuration and ownership boundaries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Pass user scope to canonical claim path validation and update the legacy scanner test seam. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace duplicated JSON examples with the native format authority and keep the integrator within the module-length guardrail. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|
| python-architect | 0 | 0 | 1 | Canonical discovery and applicability routing are coherent; one public result-type naming nit. |
| cli-logging-expert | 3 | 0 | 0 | CI inventory, printable field labels, and decoded coordinates need correction. |
| supply-chain-security-expert | 2 | 1 | 0 | Isolated probes found external-root rewriting, false clean inaccessible roots, and missed Cowork skills. |
| devx-ux-expert | 0 | 2 | 0 | 58 contract tests passed; improve protected-field remediation and coordinate semantics. |
| doc-writer | 0 | 2 | 0 | Clarify App coverage and exit1 reference; 58 tests passed, 48 links resolve. |
| test-coverage-expert | 0 | 2 | 0 | 205 targeted and 4 installed lifecycle tests passed; add mixed-strip and SARIF failure traps. |
| performance-expert | 0 | 2 | 0 | Bounded discovery and linear merging are sound; remove redundant probes and content reads. |
B = blocking-severity findings, R = recommended, N = nits. Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
-
[supply-chain-security-expert] Derive strip authority from explicit surface-root eligibility, never its display label. -- The isolated HOME reproduction rewrote an external Cowork skill that the accepted scope requires remediation to protect.
-
[supply-chain-security-expert] Report inaccessible recognized roots as incomplete coverage rather than silently excluding them. -- A chmod(0) parent containing a critical prompt produced exit 0, passed=true and zero scanned files without a diagnostic; access failures must be distinguished from deliberate symlink exclusion.
-
[cli-logging-expert] Separate hook inventory from CI violation details across passing and failing renderers. -- Current-head probes show passing text/SARIF hiding inventory and failed checks rendering not-applicable command inventory as security errors.
-
[cli-logging-expert] Escape structured field labels at human-readable rendering boundaries while preserving exact machine pointers. -- Raw U+202E survives in text and Markdown descriptions; untrusted pointer content must not control the visual interpretation of security diagnostics.
-
[cli-logging-expert] Use structured pointers as locations and explicitly qualify decoded-value coordinates in JSON, text and Markdown. -- CLI Logging and DevX both found decoded string offsets presented as source-file coordinates; SARIF already avoids the fictitious physical region.
Recommendation
Fold all in-scope panel recommendations, starting with external-root strip authority, unreadable-root false-clean behavior and CI inventory classification. Preserve the passing validation evidence, add the missing regression traps, rerun current-head validation and obtain a fresh delta panel after the fixes. The reproduced safety errors do not support ship_now; this is an advisory recommendation only, with no label, approval or merge gate.
Full per-persona findings
python-architect
- [nit] Give the result returned by scan_project_result a public type name. at
src/apm_cli/security/file_scanner.py
The typed contract is consumed across modules; a public FileScanResult name clarifies it.
cli-logging-expert
-
[blocking] Separate hook inventory from CI violation details at
src/apm_cli/policy/ci_checks.py
CheckResult.details is hidden for passing text/SARIF checks and emitted as error results for failed checks. A not-applicable command inventory entry becomes a security error when another violation fails content-integrity. Read-only current-head renderer probes reproduced both cases. -
[blocking] Sanitize structured field labels at human-readable rendering boundaries at
src/apm_cli/security/file_scanner.py
User-controlled JSON pointers are interpolated into descriptions. In-memory rendering preserved raw U+202E in text and Markdown. Preserve exact machine pointers, escape printable descriptions and Rich/Markdown display values. -
[blocking] Distinguish decoded prompt coordinates from physical file coordinates at
src/apm_cli/security/audit_report.py
JSON, text and Markdown retain unqualified decoded string line/column values beside settings.json. SARIF already omits fictitious regions. Use the pointer as location and label any decoded-value coordinates explicitly.
supply-chain-security-expert
-
[blocking] Derive strip authority from the surface root, not its display label at
src/apm_cli/security/file_scanner.py
An external managed Cowork root under isolated HOME receives a HOME-relative label, so the target-prefix strip guard fails. A manual skill named skills was rewritten by audit --strip in an isolated reproduction. Carry explicit external-root eligibility. -
[blocking] Report inaccessible recognized roots as incomplete instead of silently skipping them at
src/apm_cli/security/primitive_discovery.py
has_symlink_component returns true on OSError and containment suppresses OSError; discovery silently returns. Real chmod(0) parent fixture with a critical prompt produced exit0, passed=true, zero scanned and no coverage diagnostic. Distinguish unreadable access from deliberate symlink exclusion. -
[recommended] Reuse the deployed skills-root contract for dynamic Cowork targets at
src/apm_cli/security/primitive_discovery.py
Cowork resolver already returns Documents/Cowork/skills; SkillIntegrator uses it directly. Discovery adds mapping.subdir again and searches skills/skills. Existing generated-pattern test seeds discovery's own incorrect path. Reuse canonical destination and test actual installer-selected root.
devx-ux-expert
-
[recommended] Make structured-field remediation guidance actionable and consistent at
src/apm_cli/commands/audit.py
Warning-only TOML prompt findings recommend --strip, which refuses generically without listing filename/pointer. Use protected-aware guidance for warnings and list every blocked field before refusal. -
[recommended] Do not present decoded prompt offsets as source-file coordinates at
src/apm_cli/security/audit_report.py
A TOML prompt warning renders Location1:6 and JSON line1,column6 even though source column6 is part of the key. Match SARIF's explicit pointer/coordinate distinction.
doc-writer
-
[recommended] Do not imply filesystem discovery scans deployed Copilot App workflows at
docs/src/content/docs/reference/cli/audit.md
Actual App workflows are SQLite rows; the synthetic workflows/*.prompt.md mapping does not represent deployed files and no URI decoder scans them. Separate App from Cowork and disclose filesystem coverage limits. -
[recommended] Add incomplete coverage and refused remediation to the exit-code reference at
docs/src/content/docs/reference/cli/audit.md
Dedicated exit1 table omits incomplete coverage and refused strip, despite the new behavior section. This can misclassify access errors as malicious content.
test-coverage-expert
-
[recommended] Extend lifecycle coverage to prove strip refusal preserves eligible files too at
tests/integration/test_audit_unrecorded_unicode_lifecycle.py
Existing fixtures test protected settings and successful regular stripping separately, not a dirty SKILL.md plus protected settings in one refusal. Extend current lifecycle, no new expensive module.Proof (missing):
tests/integration/test_audit_unrecorded_unicode_lifecycle.py-- Refused mixed-content strip leaves all files and deployment records unchanged. -
[recommended] Exercise incomplete coverage through the SARIF consumer contract at
tests/integration/test_audit_primitive_contract.py
Current malformed-format tests cover JSON/CI JSON; SARIF only tests Unicode. Assert exit1, executionSuccessful=false and distinct incomplete-coverage error at the settings file.Proof (missing):
tests/integration/test_audit_primitive_contract.py-- Malformed recognized content cannot produce successful-looking SARIF.
performance-expert
-
[recommended] Compute directory status once per claim, not per surface at
src/apm_cli/security/file_scanner.py
Predicate calls is_dir before lexical relation for every nonmatching patterned surface. AST probe measured100000 calls for1000 claims and100 surfaces. Memoize one status per claim and put lexical guards first. -
[recommended] Deduplicate scan work before reading and parsing overlapping claims and discovery at
src/apm_cli/security/file_scanner.py
Tracked recognized files are read in claims then discovery; overlapping directory/explicit claims repeat work. Linear result merging only discards duplicate evidence after I/O. Use invocation-local cache keyed by path and interpretation, preserving tracked metadata and safety.
This panel is advisory. The maintainer decides whether to ship.
Generated by autopilot-pr-review-worker. This comment is AI-generated and may contain errors.
Separate CI inventory from violations, preserve denied-access coverage, bind external protection to surfaces, and share the deployed skills destination. Qualify decoded offsets, harden human rendering, deduplicate scans and extend regression traps. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|
| python-architect | 0 | 1 | 0 | Public FileScanResult and skills routing are coherent; the new cache still misses shared-root claims versus discovery. Packet validation is observed; CI remains pending. |
| cli-logging-expert | 1 | 0 | 0 | Inventory separation, decoded coordinates, and protected guidance pass bounded probes. Markdown entity decoding still bypasses pointer sanitization. CI remains pending. |
| supply-chain-security-expert | 0 | 0 | 0 | No new substantive security findings. All 80 isolated contract tests passed, including real denied-parent coverage; mixed-strip and Cowork-inside-HOME probes also passed. |
| devx-ux-expert | 0 | 0 | 0 | Static review at df7e236 found no remaining concerns in protected warning guidance, strip refusal, or decoded coordinates. No tests executed. |
| test-coverage-expert | 0 | 0 | 0 | 158 bounded tests passed. Prior mixed-strip and SARIF gaps are addressed; inspected delta regression traps are substantive. No new findings. |
| performance-expert | 0 | 0 | 0 | Both prior performance findings are addressed in cf0a1d9..df7e236. No substantive remaining findings or material complexity regression in the owned delta. |
| doc-writer | 0 | 1 | 0 | App, Cowork, coverage inventory and decoded offsets match source; one coupled exit-code promise remains stale. Static review only; docs-sync success is not established. |
B = blocking-severity findings, R = recommended, N = nits. Counts are signal strength, not gates. The maintainer ships.
Top 3 follow-ups
-
[cli-logging-expert] Escape HTML entities at the human-readable Markdown boundary and test rendered text in both tables. -- Named entities such as , , and bypass raw-Unicode sanitization after rendering. Preserve exact machine pointers in JSON and SARIF; add automated assertions against parsed Markdown output.
-
[python-architect] Separate interpretation-cache identity from attribution and prefer selected profiles over historical catalog fallbacks. -- The shared .agents/skills path can be read twice and retain Hermes attribution for a Copilot project. Add a regression proving one content read across claims/discovery and correct tracked target metadata.
-
[doc-writer] Correct the drift behavior paragraph and exit-code reference for security.audit.fail_on_drift. -- The current unconditional advisory wording misstates legitimate policy failures. Distinguish failed or unavailable drift checks promoted to exit 1 from advisory cache-miss skips.
Recommendation
Recommend folding all three remaining findings in scope, starting with rendered Markdown safety, then verifying the revised head and observing actual CI results. Preserve the prohibition on hook execution and user-configuration rewriting. Continue within the existing outer-loop budget of four, currently at iteration two. This is advisory guidance, not a label, merge gate, or authorization expansion.
Full per-persona findings
python-architect
- [recommended] Separate content-interpretation cache identity from target attribution at
src/apm_cli/security/file_scanner.py:195
The new cache key retains every PrimitiveSurface field, including target. For a Copilot project, _scan_claimed_files appends unselected catalog profiles, and primitive_surfaces uses last-wins deduplication by path/pattern: the shared .agents/skills surface is attributed to Hermes. Automatic discovery using only the selected Copilot profile produces the same path and skill_standard interpretation with target='copilot'. These keys differ, so a tracked SKILL.md is read and scanned twice despite the shared invocation cache. First-scope merging also retains the catalog-derived Hermes attribution. The metadata mismatch predates this delta, but prevents the new cache from fulfilling the prior deduplication recommendation on the normal shared-skills path.
cli-logging-expert
- [blocking] Escape HTML entities in human-readable Markdown pointer cells at
src/apm_cli/security/audit_report.py:38
_markdown_cell sanitizes raw Unicode but leaves ampersands untouched. A JSON event name containing therefore reintroduces U+200F, newline, and tab when Markdown renders the pointer. An in-memory probe against findings_to_markdown reproduced these controls in both the findings Location cell and the primitive coverage cell. This leaves the prior human-readable field-label safety finding unresolved even though the Markdown source itself contains only printable ASCII.
supply-chain-security-expert
No findings.
devx-ux-expert
No findings.
test-coverage-expert
No findings.
performance-expert
No findings.
doc-writer
- [recommended] Document policy-promoted drift failures in the revised exit-code reference. at
docs/src/content/docs/reference/cli/audit.md:295
The prior App-coverage and incomplete/refused-remediation findings are addressed. However, the surrounding exit-0 row still unconditionally classifies bare-audit drift as advisory, and the revised exit-1 row omits policy-promoted drift failures. At df7e236, src/apm_cli/commands/audit.py:1105-1115 promotes an otherwise clean run to exit 1 when the drift check fails and security.audit.fail_on_drift is enabled. This includes a drift check that could not run, while an advisory cache-miss skip remains non-failing. The unconditional promise also appears at audit.md:252-255. This retained mismatch can cause consumers to misclassify a legitimate policy failure.
This panel is advisory. The maintainer decides whether to ship.
Generated by autopilot-pr-review-worker. This comment is AI-generated and may contain errors.
…-merge-drive-head
…contract Preserve canonical emitted command layouts, safe Markdown text, shared-skill attribution, and valid native ownership fixtures. Propose optional deployed-prompt audit requirements without claiming completed specification adoption. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Spec Guardian:
|
Fold actual guardian findings without weakening conditional requirements, reporting guarantees, or pending specification adoption. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Spec Guardian:
|
Register the authority and reject all four ordinary/CI JSON/SARIF consumer bypasses without changing runtime or specification behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Advisory delta review:
|
Final bounded delta:
|
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve main's owner matrix and conformance additions; regenerate combined conformance statements. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Authorized extra pass: audit gaps closed; qualification still failsThe actual final CEO recommendation remains needs_rework, for a different, inherited failure. Both previously reported audit matrix gaps are fixed by The maintainer authorized exactly one exceptional test-only pass, cumulative outer5, not a reset. Current main was then composed cleanly through At resultant local The sole failure is The two authorized cases pass, and removing their respective detector requirements in memory causes their actual matrix assertions to fail; restoring those requirements makes them pass again. Full canonical lint/architecture checks and fifteen runtime mutation trials also pass. Those positive results do not override the failing exhaustive matrix. No unauthorized third case or sixth iteration was started. The candidate remains unpublished. The original PR still points to The guardian-reviewed normative specification remains byte-identical to66. Generated conformance statements incorporate six upstream lockfile tests; no new specification review or normative extension occurred. The guardian recommendation remains Accounting is preserved: actual outer5, Copilot2, CI recovery1, guardian2. The canonical completion schema is unchanged; its optional iterations field is omitted, with the actual count and direct authorization bound in Generated by autopilot-pr-review-worker. This comment is AI-generated and may contain errors. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
APM CEO (last actual pass: dc89f5fe) |
0 | 0 | 0 | Historical technical ship_now; no new persona execution is claimed for this mechanical repair. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are historical review signals, not adoption approvals.
Recommendation
Retain the prior technical ship_now recommendation after verified additive conflict repair, bounded test-model reconciliation and this result head's own validation. Keep the specification-adoption hold and normal repository approvals separate. No merge, auto-merge, review approval, queue action or policy waiver was performed.
Full per-persona findings
APM CEO - historical, not rerun
The actual prior composition assessment found no follow-ups at dc89f5fe. Current conflict resolution preserves the reviewed audit implementation while incorporating the already-landed upstream changes; fresh execution, not an invented review, qualifies the resulting tree.
Current-head execution
The unchanged combined functional selection executed at 0cac8097: 3,357 passed, 2 existing skips, 2 subtests passed in 1,256.77 seconds. JUnit contains 3,359 unique named outcomes. All seven current-main lint gates and the architecture boundary check passed. The intermediate 78b8b5a4 run's 3,350 passes, seven stale-oracle failures and two skips remain separately attributed.
The full run combines prior audit coverage with every incoming changed test module: HOME/APM_HOME aliases, ref freshness, Claude native deduplication and policy scanning, protected explicit cleanup, Cursor runtime/static values, real installed-CLI lifecycle snapshots, native evidence/provenance owners, complete owner mutation matrix and frozen registry, conformance and quality.
Nine physical on-disk guard/registry removals caused assertion failures and then passed after byte-exact restoration. They cover root prompt discovery, cleanup hash/current-content protection, both audit owner records plus the native projection owner, and native-eligibility removal against each of the three corrected lifecycle locations. Fifteen process-local audit mutations and three actual-matrix detector-removal controls were also rerun. No mutant was committed. These probes do not claim to mutate every incoming upstream test independently.
Strict terminal verification returns verified=true, terminal_evidence_required=true, 8 touched owners / 31 actual passing functional IDs. Incoming native/provenance owners are retained and exercised in the whole registry/matrix and lifecycle selection; they are not falsely classified as PR-specific changes.
All six identity-checked current-head workflow runs completed successfully: CI, spec conformance, merge gate, docs, NOTICE and CodeQL. The qualification snapshot observed 19 SUCCESS, 2 NEUTRAL, 1 SKIPPED, with GitHub MERGEABLE/BLOCKED. Only workflow execution was approved; no human review approval occurred.
Canonical completion SHA256: 56c63303cf0b34c4171d20ed81b32ff577997a179649d60621831a50407ee493. The matching iteration-exception.json, scenario-evidence.json, strict owner receipt and unchanged-spec crosswalk bind this head's execution and historical identity. The final terminal seal includes the live publication readback and current CI snapshot.
Specification and historical evidence
All 34 guardian-identity artifacts remain byte-identical to reviewed 66ba9ea6; no third guardian was run. Fifty prior composition-identity files remain identical to dc89f5fe. Four artifacts changed faithfully with main: the owner matrix, lockfile conformance tests and generated CONFORMANCE.json/CONFORMANCE.md. The generator reproduced the combined statements; these four are explicitly not claimed byte-identical to the prior packet.
The accepted dc89f5fe packet is preserved under qualified-dc89, including completion SHA256 79388c7f25b3c0b8a2b88120245b48342ca4bece9c3c0c45ea4ff47bc4e76b7a and seal db440c48b8568a60ffcd0e9e163e1ec4f44838aa7412143b7996496ba8b26f6c. It remains historical, not rebound to the repaired head.
Disclosure
Actual cumulative accounting is outer 9 / Copilot 2 / CI recovery 1 / guardian 2, under the explicit conflict request and continuous mergeability authorization; no reset. The unchanged canonical schema omits only its unsupported optional iteration field, with the true count and completion hash bound in iteration-exception.json. The seven failures from intermediate 78b8b5a4 remain archived under that SHA, not relabeled as current-head results.
Existing conformance skips, the earlier timeout with independent passing evidence, the incomplete tool-starved docs-sync classification and the preliminary-scanner boundary of the user-scope mutation remain disclosed. Local validation uses macOS and the editable source CLI; inherited native-lifecycle proof is not an all-platform shipping claim. Actual CI jobs provide separate platform evidence.
This panel is advisory. Human specification adoption and normal repository approval requirements remain separate and unchanged.
Generated by autopilot-pr-merge-worker. This comment is AI-generated and may contain errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve additive owner-registry and root-context helper conflicts by retaining both parent contracts unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve exact native revisions and no redundant Claude root, with complete snapshot assertions and isolated Hermes compilation writes after main composition. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
4e916ec
into
microsoft:main
fix(audit): separate deployed primitive discovery from prompt checks
TL;DR
Automatic audit discovers recognized primitive files and native hook entries without walking entire tool roots. It checks prompt documents and documented decoded prompt fields, not executable commands or unrelated configuration. Unknown or unreadable recognized content fails with explicit incomplete coverage; discovering a manual hook does not imply unsafe content, ownership, or hash verification.
Important
Fixes #2961 under the maintainer-approved scope and revision-before-panel contract. Review contact: Daniel Meppiel (@danielmeppiel).
Warning
The bounded OpenAPM 0.1.42 amendment is proposed, not adopted. Its actual guardian recommendation is
fold_and_ship(8/10), but Section 9.3 human approvals and the subsequent 14-day comment period remain outstanding. Technicalship_nowand green CI do not waive that process.Section 9.3 requires a spec-labelled issue and at least two non-author approvals covering implementation and consumer/integrator experience. Linked issue #2961 lacks the spec label, and the observed PR review history has no qualifying approvals. The 14-day clock follows that panel approval, not the September 30 proposed-history entry. No earliest adoption date is established.
Problem (WHY)
The maintainer-approved contract is the authority. Explicit per-format rules follow Agent Skills' guidance to provide "project-specific conventions, domain-specific procedures, non-obvious edge cases, and the particular tools or APIs to use", rather than infer native formats from arbitrary strings.
Approach (WHAT)
Implementation (HOW)
security/primitive_discovery.pysecurity/file_scanner.pyintegration/targets.pypromptand Codexdeveloper_instructionsTOML fields in existing mappings; reuse canonical skill destinations.hook_contract.py,integration/hook_integrator.py,integration/hook_native_formats.pycompilation/root_context_protection.pycommands/audit.py,policy/ci_checks.py,policy/models.pysecurity/content_scanner.py,security/audit_report.pyinstall/manifest_reconcile.py.apm/architecture/owners/hooks-integrations.json,scripts/architecture_linter/checks/audit_discovery.py,groups/mutation_writes.pytests/integration/test_audit_primitive_contract.py,test_architecture_audit_discovery.py,test_audit_unrecorded_unicode_lifecycle.pytests/integration/test_architecture_owner_rule_mutations.pydocs/src/content/docs/reference/cli/audit.md,reference/baseline-checks.md,enterprise/security.md, apm-usagecommands.mddocs/src/content/docs/specs/openapm-v0.1.md, requirements manifest, policy conformance tests/generator,CONFORMANCE.*deployed-prompt-auditcapability and conditionalreq-pl-019/020, backed by real conformance tests and explicit pending adoption. No schema redesign.Unqualified production paths above are under
src/apm_cli/. The discovery implementation is independent of ownership evidence. Decoded prompt offsets are not represented as physical source coordinates.Diagrams
Dashed nodes are the new discovery/applicability stages; command-only inventory reaches reports without becoming a Unicode finding.
flowchart LR subgraph Discovery["Discovery"] T["Target profiles and hook registry"] --> S["primitive_surfaces"] L["Recorded deployment claims"] --> U["scan_project_result"] S --> U end subgraph Applicability["Applicability"] U --> P["Documented prompt text"] U --> C["Command-only inventory"] U --> E["Unreadable or unsupported content"] end subgraph Reporting["Reporting"] P --> F["Unicode findings"] C --> R["Audit and CI reports"] E --> I["Incomplete coverage: exit 1"] F --> R I --> R end classDef new stroke-dasharray: 5 5; class S,U,P,C,E,I new;Trade-offs
SKILL.md, not arbitrary resources/executables. Explicit recorded prompt documents retain claim-path coverage.--fileremains user-directed; discovery grants no configuration rewrite authority.trackeddoes not certify per-entry ownership. Canonical ledger/hash and owned-hook projection checks retain their evidence requirements.Benefits
Validation
Published exact head:
0cac8097a9bf3e4a4a1cc8a01cc2f303787642ad.Composed main:
73d6d0e280d9d898bb9ab84c7ec28f4348691199.Last actual narrow composition CEO: technical
ship_nowatdc89f5fe, not a fresh review of this head. This pass repairs additive conflicts and stale lifecycle-test expectations; no new panel or guardian is claimed. Previous full/delta qualification remains historically attributed.Observed exact-head qualification CI: 19 SUCCESS, 2 NEUTRAL, 1 SKIPPED. CI, spec conformance and merge gate passed; all six approved workflow executions completed successfully. Prior-head CI is not reused. CodeQL's aggregate and eligibility advisory are neutral; docs deployment is skipped. GitHub reports MERGEABLE/BLOCKED, not permission to merge.
The unchanged canonical completion schema validates. Strict deterministic owner verification returns
verified: true,terminal_evidence_required: true,touched_owner_count: 8, with 31 actual exact-head passing consumer IDs. Incoming native/provenance owners remain in the full registry and mutation matrix.The combined
validate_composition.pyselection and seven-gateverify_revision.shproduced:The unchanged combined selection covers the WHOLE exhaustive owner matrix, frozen inventory/registry, affected audit/native/policy/report suites, installed CLI/lifecycle, conformance and quality, plus every incoming changed test module. It retains HOME/APM_HOME aliases, ref freshness, native instruction/policy/cleanup, Cursor runtime/static values and provenance coverage from #2876, #3119 and #3116.
Merge
78b8b5a4resolved two actual additive conflicts:hooks-integrations.jsonpreserves audit and native projection owners;root_context_protection.pypreserves audit filename discovery and protected-root/cleanup helpers. All 40 incoming and two prior helper function ASTs retain their parent definitions. The successor changes only three lifecycle test locations and related docs, not runtime/specification code. Seventy-seven incoming paths remain upstream-identical; fourteen contain faithful unions or the documented test-model correction. Both changelog entries and the single marketplace mutation case remain.The intermediate merged-head run had seven stale expectations for redundant Claude roots. Corrected tests require exact installed native revisions, no redundant
CLAUDE.md, and unchanged complete snapshots; mixed Claude/Hermes fixtures allow only HermesAGENTS.mdwith the correct revision. Unmatched fallback, policy refusal and protected cleanup remain covered. The failed run stays archived under78b8b5a4.Additional evidence and limitations
66ba9ea6: actualfold_and_ship, four scores 8/10. All 34 guardian artifacts remain byte-identical. Fifty previous composition artifacts remain identical todc89f5fe; four changed faithfully with main: lockfile conformance tests, the owner matrix and generatedCONFORMANCE.json/CONFORMANCE.md. Combined statements were regenerated and the suites rerun. The SHA256 crosswalk distinguishes unchanged identity from upstream deltas; no third guardian was run.dc89f5feand earlier packets remain immutable historical evidence, not rebound.mmdcfor this body. Local proof is macOS/editable-source CLI, not an all-platform shipping claim; actual CI platform execution is separately attributed.Scenario Evidence
test_audit_primitive_contract.py::test_excluded_transcripts_are_not_walked_or_read::test_shared_settings_command_inventory_is_visible_and_non_failing,::test_native_command_formats_are_inventory_not_prompt::test_decoded_prompt_fields_fail_without_scanning_unrelated_settings::test_unknown_or_malformed_recognized_content_never_claims_success,::test_unreadable_recognized_file_is_reported::test_registered_prompt_patterns_are_discovered,::test_user_scope_claims_keep_tracking_metadata::test_non_prompt_scripts_keep_hash_and_ownership_checks::test_symlink_prompt_is_never_read,::test_audit_and_failures_preserve_complete_workspace_snapshottest_audit_unrecorded_unicode_lifecycle.py::test_native_hook_discovery_across_reinstall_update_and_user_scopetests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_fieldtest_architecture_owner_rule_mutations.py, including registry equality and attributed violationstest_generated_lifecycle_state_machine.py::test_generated_lifecycle_mandatory_replay,test_required_lifecycle_state_machine.py::test_required_global_audit_rule_matrix_for_external_roots,test_ownership_invariant_lifecycle.py::test_global_update_preserves_owned_external_skill_targetsUnqualified test filenames are under
tests/integration/; abbreviated methods refer totest_audit_primitive_contract.py.How to test
uv run --frozen --extra dev pytest -q tests/integration/test_audit_primitive_contract.py tests/integration/test_architecture_audit_discovery.py tests/integration/test_architecture_owner_rule_mutations.py; expect every case, including exhaustive equality, to pass.APM_E2E_TESTS=1 APM_BINARY_PATH="$PWD/.venv/bin/apm" uv run --frozen --extra dev pytest -q tests/integration/test_audit_unrecorded_unicode_lifecycle.py; expect real CLI preservation cases to pass..claude/settings.json;apm audit --no-drift --format jsonshould show non-applicable inventory without command execution."\u202e"to a documented nested prompt handler; expect exit1 and its exact pointer. Automatic--stripmust refuse configuration changes.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com