Repository navigation
Code proof admission: reject duplicate and noncanonical entries inside each Git tree #82
Description
Confirmed implementation gap
Revalidated at PR #81 head a4761daa9fa87026ea106a3d722e7299b3de3b8b on 2026-10-22. This records a previously prepared local review item in the repository; it is not a new semantic rule.
src/devostasis/codeanalysis/bundle.py::admit and src/devostasis/codeanalysis/history_proof.py::tree validate the object digest, mode, name and duplicate final file paths, but do not reject repeated entry names within one tree or noncanonical entry order. Two directory entries with the same name and disjoint children evade the final-file-path duplicate check. A correctly hashed but unsorted tree also passes these loops. Hash consistency alone does not establish an unambiguous Git path inventory.
Reproduction and impact
Use a disposable local repository with raw tree objects, without running repository source. Construct either (a) two dir tree entries pointing to different subtrees, or (b) entries z.py, a.py in that order. git hash-object -t tree -w --stdin can store these objects. Recursive listing can show paths that normal Git path lookup does not resolve consistently; git fsck --strict reports duplicateEntries or treeNotSorted. The current walker loops lack the corresponding structural rejection.
The prepared local regression artifacts cover SHA-1 and SHA-256, current trees and historical ancestors. Prior extracted-function checks are limited evidence, not full-product acceptance. Full checkout tests and historical replay have not been run in this review environment.
Scope
Add per-tree name uniqueness and Git canonical ordering checks to both admission paths. Keep existing byte/object/expansion limits, valid modes, supported formats, offline replay, and explicit unavailable evidence. Do not change Vital rules, thresholds, permanent conformance identifiers, source-selection policy, or execute observed source.
Acceptance
- Duplicate directory names, file/directory name collisions and noncanonical order are rejected independently in current and historical trees.
- Parameterized regressions cover SHA-1/SHA-256 and both paths; include ordinary files, directories, links and submodules as valid controls.
- Ordering matches Git's byte ordering with the directory terminator, not locale or Unicode sorting.
- Existing v1/v2 examples retain canonical bytes and verify; full pytest, code examples and history proof replay pass at the exact fixing head.
- Independent review covers the final head before merge into
release/0.6.0; Release 0.6.0: offline repository analysis and executable assurance #75 is not released ahead of this gate.
Related: #72, #73, #81. Those issues track the broad producer/proof capability; this issue isolates the concrete tree-admission defect. Existing open and closed issue search and #81 discussion were checked before filing.
Activity
andy-zen-dev commentedon Oct 22, 2026
Concrete repair for the current source/history walkers
Apply to the existing #81 development path based on
a4761daa9fa87026ea106a3d722e7299b3de3b8b, then propagate through release/0.6.0. This is a prepared patch, not a code commit, release approval or issue resolution.Per-tree state is reset for every expanded object. Existing cycle, byte/object/leaf limits and historical work-budget early return are retained. A capped historical walk is still incomplete rather than a successful full proof.
Regression shape: parameterize SHA-1/SHA-256 × source/historical ancestor × duplicate directory/unsorted names (8 negative cases). Build the malformed raw tree only in a temporary test repository; create an ordinary child commit for historical-only cases so source-tree admission cannot mask the historical path. Use
tests/test_code_analysis.py'sgit,commitandanalyzehelpers, whose current signatures were re-read. Require the specific validation error rather than any failure. Retain ordinary canonical-tree controls and verify v1/v2 examples byte-for-byte.The updated local candidate also adds the per-tree uniqueness/order rule to docs/spec/code-analysis.md and contains those eight project tests. Full project execution is NOT_RUN in this environment; limited extracted-walker probes do not close the release gate. Do not apply earlier duplicate-only and canonical-order patches together: this single guard supersedes both.
andy-zen-dev commentedon Oct 23, 2026
2026-10-23 addendum to #82: reserved Git name parity (HEAD
a4761daa9fa87026ea106a3d722e7299b3de3b8b)The source and historical tree walkers at this exact HEAD validate names with only
b"/" not in name and name not in (b".", b"..")insrc/devostasis/codeanalysis/bundle.py::admitandsrc/devostasis/codeanalysis/history_proof.py::tree. Neither validates Git-reserved component spellings. The earlier proposed patch in this Issue's comment6334248799also only addresses duplicate names/order; and a later prepared local supplement checked only literal.git, which is too narrow.A new native Git 2.47.3 offline oracle used separate disposable SHA-1 and SHA-256 repositories, a valid blob, and raw deliberately hashed trees for each case. 24/24 expected name classifications matched
git fsck --strict(native Git outcome only; NOT Devostasis integration):.git,.GIT,.GiT,.git,.git.hasDotgitGIT~1,.git:evilhasDotgit.gitignore,.gitmodules,code.py,git,.git~1Concrete extension to #82's acceptance: in both current-source and historical-ancestor tree admission, reject Git-reserved name spellings consistently with the declared portable Git tree safety policy, not just literal
.git. Use a common deterministic byte-level component predicate or explicitly shared structural validator so the walkers cannot diverge. Keep valid.gitignore,.gitmodulesand ordinary names accepted. Cover both object formats and positive/negative controls in real full-pathcode verifyregressions; ensure historical bundle replay stays intact. Do not loosen source path safety or infer that an accepted hash proves Git name validity.Limit: this probe establishes the listed Git 2.47.3 behavior only. It does not exhaust HFS/NTFS reserved-name behavior, prove end-to-end acceptance in Devostasis, or certify a fixing commit. Full pytest and upstream CI remain NOT_RUN/NOT_PASS for these new cases. No semantic Vital/policy/version change is requested.
andy-zen-dev commentedon Oct 23, 2026
2026-10-23 — #82 additional structural admission finding:
.gitmoduleshas mode-specific Git validityHEAD independently examined: PR #81
a4761daa9fa87026ea106a3d722e7299b3de3b8b; current/history raw-tree readers aresrc/devostasis/codeanalysis/bundle.py::admitandhistory_proof.py::tree. This extends the existing #82 comment about.git-name aliases; it is not a separate Issue or a contract change.New observation: Git 2.47.3
git fsck --strictaccepts.gitmodules(and its tested case/NTFS aliases) as a regular blob at modes100644or100755, but rejects the same name as a40000directory,120000symlink or160000gitlink. In particular, a positive.gitmodulesblob is not a positive.gitmodulesdirectory: an earlier local draft accidentally treated the directory as valid. Git reportsgitmodulesBlob/gitmodulesSymlinkviolations for these malformed shapes.The native-Git matrix covered SHA-1 and SHA-256, names
.gitmodules,.GITMODULES,.gitmodules,.gitmodules.,GITMOD~1,.gitmodules:evil, with.gitmodulesXand.gitignorecontrols, across all five supported raw-tree modes: 80/80 observed cases. It extends the previously reported 24/24 Git-name cases. Results are native Git evidence only; they are not full Devostasis verification.Prepared implementation candidate: centralize per-entry validation in one shared pure helper used by both walkers. In addition to the already-required exact byte-order comparator (append
/to directory names), within-tree uniqueness, valid modes and.gitaliases, normalize the tested.gitmodulesaliases using ASCII case-folding/trailing dot-or-space handling and require mode in(100644,100755). The helper's local checks matched 24/24 + 80/80 native-Git observations and 14/14 local structural controls. An uncommitted unified source patch and two-reader regression tests were prepared.git apply --check/apply passed only against an exact-context fragment mirror, not a real checkout; Python syntax compilation passed.Acceptance/verification still needed: test malformed and positive
.gitmodulesentries through both actualbundle.admitand historicaltreepaths (SHA-1/SHA-256), check against existing v1/v2 examples, run full offline pytest and historic replay at the fixing HEAD, then obtain a later independent review. Do not claim all HFS/NTFSfsckcases are implemented: the shared helper covers only a bounded verified family. No threshold, historical identity, Vital or model-free rule change is requested.Current restrictions: the observed GitHub identity has
pull=true,push=false, so this comment is the durable handoff, not a pushed branch/PR, a passing CI gate, or a release approval.andy-zen-dev commentedon Oct 23, 2026
2026-10-23 — correction to #82 Git-name test authority:
.gitmodulesgitlink results depend on the referenced object typePR #81 exact source head:
a4761daa9fa87026ea106a3d722e7299b3de3b8b(unchanged on re-read). This corrects the over-broad statement in my earlier #82 comment 6361592793 thatgit fsck --strictuniversally rejects mode160000for.gitmodulesaliases. That statement is not generally true.Using Git 2.47.3 and disposable repositories, I generated correctly hashed raw tree/commit objects containing an entry
160000 <alias>\0<OID>, then rangit fsck --strict --no-reflogson the referenced root commit. The critical difference is what object that OID identifies:fsck --strict160000 .gitmodules160000 .gitmodulesgitmodulesBlob: non-blob found at .gitmodulesThe difference holds for six aliases (
.gitmodules,.GITMODULES,.gitmodules,.gitmodules.,GITMOD~1,.gitmodules:evil) in both SHA-1 and SHA-256: 24/24 expected type-sensitive cases reproduced.Important interpretation: a Gitlink semantically denotes a commit, not a blob. Native fsck accepting a blob-backed mode-160000 entry does not make it a well-typed Gitlink. Its special
.gitmodulescheck can behave differently depending on referenced object type. The prior 80/80 mode/name matrix and statement about universal rejection must therefore be read with this type-correct-target precondition, rather than as a proof thatfsckvalidates name/mode pairs in isolation.After making that test precondition explicit (mode
160000uses an actual commit OID, tree mode uses a tree OID, file/link modes use blob OIDs), the independent local proposed name/order validator matches 165/165 scoped, parameterized Git-name/mode/order assertions. This is local Git-oracle validation only, not a passing Devostasis checkout or complete Unicode/HFS/NTFS parity claim.Bounded implementation guidance: maintain fail-closed structural admission for #82; it is legitimate for the product to reject dubious
.gitmodulesgitlink entries even whenfsckis permissive about blob-backed OIDs. Do not relax a conservative check merely to match this incidental native-fsck behavior. But make the test fixture's target-object type explicit, and distinguishGit agrees for validly typed fixturesfromexact parity for every malformed cross-type object. Regressions still must exercise both realbundle.admitand proven historical tree paths, SHA-1/SHA-256, full pytest/example replay and a later exact-head independent review before merge.This is a correction of evidence qualification in the existing Issue, not another issue, a semantic Vital change, an upstream test PASS or proof of release readiness. No source write/PR/merge was performed.
andy-zen-dev commentedon Oct 23, 2026
2026-10-23 — #82 addendum:
.gitattributesneeds a distinct, mode-sensitive Git-tree admission ruleScope / exact HEAD: PR #81
a4761daa9fa87026ea106a3d722e7299b3de3b8b. I re-readsrc/devostasis/codeanalysis/bundle.py::admit(blob3ac0d6c64c2a8ba7c6310e73bf6dfb9e2120d1f7),src/devostasis/codeanalysis/history_proof.py::tree(blobf5f0445cba726571b88ac77fec6f440e58e22c78), and the shared path predicate. Both raw-tree walkers check slash / dot / dotdot but not the special.gitattributesname+mode combination. The pending #82 duplicate/order repair also omits this case. No separate Issue is needed.Independent native-Git evidence: Git 2.47.3, 80 disposable repository cases: SHA-1 and SHA-256 × 8 tested tree-entry spellings × 5 supported modes (
100644,100755,120000,160000,40000). Entries were byte-encoded raw trees written withgit hash-object --literally -t tree -w --stdin, referenced from ordinary well-formed root commits, and examined withgit fsck --strict --no-reflogs. Referenced OIDs were type-correct: blob for regular/symlink entries, commit for gitlink, tree for directory. Outcomes agreed between hash formats.100644/10075512000016000040000.gitattributes,.GITATTRIBUTES,.gitattributes,.gitattributes.,GITATT~1,.gitattributes:evilgitattributesBlob)gitattributesBlob).gitattributesX,.gitignore24/80 cases were Git-rejected (six special spellings × two invalid modes × two hash formats). This is important because
.gitattributesis not identical to.gitmodules: the tested.gitattributessymlink is accepted by Git, whereas the prior #82.gitmodulessymlink cases are rejected. A shared validator should not blanket-reject all symlinks for both names.Implementation-facing acceptance addition: extend the shared structural tree-entry admission proposed in #82 to handle these special spellings/modes consistently in both current-tree and historical-tree paths. Add positive and negative SHA-1/SHA-256 regressions at the actual
bundle.admitand history-proof interfaces; preserve positive.gitignore/.gitattributesXand ordinary symlink cases. Preserve canonical tree bytes and historical replay. Do not infer that hash verification implies Git-shape validity; do not modify Vital semantics.Evidence limit: this verifies native Git behavior and the missing explicit name/mode admission check by exact-head source inspection, not a completed
devostasis code verifyexploit, full pytest, bundle replay, fix commit, or release approval. All those remain NOT_RUN / NOT_PASS as applicable. Existing #82 is still open.