Skip to content

feat: add canonical verification project discovery - #180

Draft
larimonious wants to merge 5 commits into
feat/dd078-slice-1b-evidence-reportfrom
feat/dd078-slice-2a-project-manifest
Draft

feat: add canonical verification project discovery#180
larimonious wants to merge 5 commits into
feat/dd078-slice-1b-evidence-reportfrom
feat/dd078-slice-2a-project-manifest

Conversation

@larimonious

Copy link
Copy Markdown
Contributor

Summary

  • add canonical project-root resolution and a versioned exhaustive verification manifest
  • add deterministic, root-confined project-resource discovery with fail-closed link, alias, overlap, and build-output handling
  • share safe nearest-manifest loading with existing config and secret behavior

Validation

  • focused verification manifest tests: 33/33
  • full nextest: 2,044/2,044
  • DD-078 plan tests: 2/2
  • doctests and dev-release build
  • formatting and diff checks
  • exact-head immutable cold review: PASS
  • Windows hardlink helper cross-compiled for GNU and MSVC targets

Stack

Depends on #179 (DD-078 Slice 1B).

@larimonious
larimonious changed the base branch from feat/dd078-slice-1b-evidence-report to main July 31, 2026 04:32
@larimonious
larimonious changed the base branch from main to feat/dd078-slice-1b-evidence-report July 31, 2026 04:34
@larimonious
larimonious changed the base branch from feat/dd078-slice-1b-evidence-report to main July 31, 2026 04:35
@larimonious larimonious reopened this Jul 31, 2026
@larimonious
larimonious changed the base branch from main to feat/dd078-slice-1b-evidence-report July 31, 2026 04:35
@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces canonical project-root resolution (src/project.rs), a versioned exhaustive verification manifest (src/verification/manifest.rs), and deterministic project-resource discovery (src/verification/discovery.rs), tied together by a shared, security-hardened nearest-manifest loader refactored into src/config.rs. The new loader is simultaneously adopted by the existing lint-strict config reader and the secrets declaration reader, replacing bespoke ancestor-walk loops in both places.

  • ProjectRoot::discover performs a dual lexical + canonical manifest search and rejects paths whose two roots disagree, guarding against symlink-based project confusion. The manifest itself is verified not to be a symlink or hardlink at two independent points (pre- and post-selection).
  • VerificationManifest::load parses a [verification] TOML table, enforces extension-to-class consistency (.intentIntent, .tntApplication|Verification|Support), and rejects build-output paths, symlink escapes, and hardlinks for every configured resource.
  • ProjectDiscovery::discover combines automatically-found .intent files (excluding hidden dirs, node_modules, and build outputs) with manifest-configured files into one canonical, lexicographically sorted inventory, catching duplicate canonical targets across both sources.

Confidence Score: 5/5

Safe to merge. The change adds new hardening infrastructure that replaces several ad-hoc manifest walkers with a single, consistently fail-closed implementation.

All three adoption sites (lint-strict config, secret declarations, and the new verification discovery) now share one security-hardened manifest loader. The dual lexical/canonical root check, the directory-symlink rejection, and the fail-closed unknown-platform hardlink guard all look correct and are covered by 33 focused tests plus the full 2k+ nextest suite. The one observation worth addressing before the next iteration is that metadata and canonicalize failures in the manifest resource validator discard the source IO error, making permission-denied failures indistinguishable from missing-file failures at the user level — but this does not affect correctness or security.

Files Needing Attention: No files require special attention for merge safety; src/verification/manifest.rs has a minor diagnostic quality gap in how IO errors from metadata and canonicalize are reported.

Important Files Changed

Filename Overview
src/config.rs Adds shared load_project_manifest / load_project_manifest_file helpers and cross-platform has_multiple_hardlinks; the non-unix/non-windows fallback now correctly fails closed (returns true), addressing the previously noted gap.
src/project.rs New ProjectRoot::discover performs dual lexical + canonical manifest search and applies a second hardlink/symlink guard before committing the result; AmbiguousRoots error uses a non-root directory as one of the two "roots" in some arms (noted in previous outside-diff comment).
src/verification/manifest.rs Versioned manifest parser with exhaustive path-validation (path escape, build-output, existence, hardlink, symlink-escape, extension-class consistency, duplicate/overlap detection); all critical checks are ordered correctly and covered by tests.
src/verification/discovery.rs Deterministic recursive discovery that skips build-output dirs, hidden dirs, and node_modules; explicitly rejects directory symlinks (previously silently dropped per previous thread), and catches escape/hardlink/build-output for file symlinks; duplicate canonical targets detected across both auto and configured sources.
src/stdlib/secrets.rs Refactored load_declarations to use the shared load_project_manifest helper; symlinked or hardlinked manifests now return SecretDeclarations::Invalid (fail-closed) instead of being transparently read, which is the intended new behavior.
src/main.rs read_project_config_strict simplified to use load_project_manifest; non-regular manifests now return false (strict disabled) rather than potentially reading a symlinked config.
tests/verification_manifest_tests.rs 33 focused tests covering root resolution, manifest loading, resource validation, and discovery; includes explicit regression tests for symlink escapes, hardlinks, directory symlinks, build-output aliases, and the cross-root ambiguity cases.
Cargo.toml Adds windows-sys 0.59 as a Windows-only dependency for the hardlink helper (GetFileInformationByHandle); feature flags are minimal and correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ProjectRoot::discover start] --> B[load_project_manifest lexical walk]
    A --> C[canonicalize path]
    C --> D[load_project_manifest canonical walk]
    B --> E{Match lexical + canonical results}
    D --> E
    E -->|Both found, roots agree| F[Re-verify manifest: not symlink / hardlink]
    E -->|Both found, roots differ| ERR1[AmbiguousRoots error]
    E -->|Only one found| ERR1
    E -->|Neither found| ERR2[ManifestNotFound error]
    F --> G[ProjectRoot]
    G --> H[VerificationManifest::load]
    H --> H1[Re-check manifest not symlink / hardlink]
    H1 --> H2[Parse TOML verification table]
    H2 --> H3{version == 1?}
    H3 -->|No| ERR3[UnsupportedVersion]
    H3 -->|Yes| H4[Sort files by path]
    H4 --> H5[For each file: validate path, check existence, hardlinks, symlink escape, build-output, extension-class]
    H5 -->|Any check fails| ERR4[ManifestError]
    H5 -->|All pass| I[VerificationManifest]
    G --> J[ProjectDiscovery::discover]
    I --> J
    J --> J1{manifest.project_root == root.path?}
    J1 -->|No| ERR5[ManifestRootMismatch]
    J1 -->|Yes| J2[collect_automatic: recurse intent files, skip hidden dirs / build outputs / nested manifests]
    J2 --> J3[Check auto-found .intent files: symlink escape, directory symlink, build-output alias, hardlink]
    J3 --> J4[Add manifest-configured files, check AutomaticManifestOverlap]
    J4 --> J5[Canonicalize all paths, check DuplicateResource by canonical]
    J5 --> J6[ProjectDiscovery: sorted DiscoveredFile list]
Loading

Reviews (3): Last reviewed commit: "fix: report lexical project root ambigui..." | Re-trigger Greptile

Comment thread src/config.rs
@larimonious
larimonious changed the base branch from feat/dd078-slice-1b-evidence-report to main July 31, 2026 21:18
@larimonious larimonious reopened this Jul 31, 2026
@larimonious
larimonious changed the base branch from main to feat/dd078-slice-1b-evidence-report July 31, 2026 21:18
@larimonious
larimonious changed the base branch from feat/dd078-slice-1b-evidence-report to main July 31, 2026 21:29
@larimonious larimonious reopened this Jul 31, 2026
@larimonious
larimonious changed the base branch from main to feat/dd078-slice-1b-evidence-report July 31, 2026 21:29
@larimonious
larimonious marked this pull request as draft August 5, 2026 01:13
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