Repository navigation
方向記録版todo.exe 1本の更新資格を独立readerで照合する(ToDo3463・親3190) - #409
mashfromband wants to merge 25 commits into
Conversation
Adds the spec (Issue #408), the CLI contract, the fixed production pins and synthetic Git/file/receipt acceptance tests. verifyUpdate is a stub, so every verification test fails for the intended reason.
…ader The reader pins the installed leaf, asks the deployed guard's existing capacity purpose to qualify the f871 baseline, and only then hashes it. It binds the d439 candidate to the fixed merged source 0e16e81, its tree, all 126 compiler inputs, the original build receipt, log and dep-info, checks the compiler closure, classifies every read file with the deployed guard, and rechecks every pinned handle and the worktree before returning. It replaces nothing and always reports data_access_granted false. The shared guard is unchanged. Refs #408
Runs the module's race tests, vet and build on the self-hosted Windows runner, which has the Git, hard link and junction support the synthetic fixtures need. Refs #408
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 8d1e7b2a-eaa7-4284-a620-ba0648cd30d0) |
Review ToDo3482 (Must): git diff HEAD read every tracked file before the guard classified it. These tests record every Git call, hold a protected non-input README unreadable, and require the reader itself to tie pinned input bytes to the fixed blobs. checkedOut and gitBlobID are stubs here (Red).
Git now reads only refs, configuration and objects (rev-parse, remote get-url, merge-base, ls-tree). The whole-tree git diff HEAD is gone; the reader hashes each guard-classified, pinned input itself and requires its bytes to be the fixed blob, either as is or with every LF written as CRLF. Tracked files outside the inputs are never classified or read. The spec now lists the argument reason and states exactly what the closure check covers.
Adds the review result (Must 1, Suggestion 2, Nit 1), the Red/Green/mutation measurements of the fix and the pre-merge observation of the reader built from 74c87e3, and separates official CI, re-review, merge and the post-merge check that remain.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 6f61684f-a4cf-4091-a8ab-7739471865e8) |
Review ToDo3491 (Must): Git still read configuration, and so any include, before the guard classified it. The fixture is now a linked worktree like the real source. These tests demand exactly three object reads with exact arguments, ignore a global include of a protected unreadable file, refuse repository includes, includeIf and URL rewrites before Git runs, and read the worktree link, HEAD, refs, configuration and shallow boundary through the guard. isolatedEnvironment is a stub here (Red).
Review ToDo3491 (Must): `git remote get-url` and the other source checks let Git read the system, global and repository configuration, and so any include.path or includeIf target, before the guard classified it.
The reader now reads the worktree link, its registration (commondir, gitdir), HEAD, the branch and origin/main refs (loose, else packed-refs), the repository configuration and the shallow boundary itself. The guard classifies each file first; the reader reads it once from a plain single-link handle and closes it so the other worktrees can keep updating the shared repository. A strict parser reads the configuration as Git does and refuses include, includeIf, url, core keys other than those of a normal non-bare repository, extensions other than worktreeConfig, and syntax it cannot read the same way (continuations, text after a header, the old [a.b] form, unknown escapes, open quotes, control characters). config.worktree, info/grafts and objects/info/alternates must be absent.
Git then runs in an empty scratch repository whose only outside data is the classified object store and a copy of the shallow boundary, with no GIT_* inheritance and no system or global configuration. It runs exactly rev-parse <head>^{tree}, merge-base --is-ancestor <head> <origin/main id> and ls-tree. The recheck reopens every released metadata file and compares identity and SHA-256, and requires every absent file to stay absent.
Tests added with the fix: a shallow clone whose parent object is missing, a bare or core.worktree configuration, refs packed or alternates added or a ref rewritten in place before the recheck, Git's own reading of each accepted configuration (git config --list) and the packed-refs format. go test -race: 153 passed, 0 failed. Eleven mutants of the new checks are each killed by the intended test.
Steps 5 and 10 now describe the metadata the reader reads itself, the scratch repository Git runs in with only the object store, and the recheck of released and absent files. Adds the ToDo3491 review, the second Must fix with its Red, Green and mutant results, and the pre-merge observation of the reader built from 2f804f1.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 91482fb5-9e3e-4766-b52e-353d12c6f2fc) |
…Git does Review ToDo3505 found two Musts. Git reads objects/info/alternates of the shared store, so an alternates file added while the reader runs and removed before the recheck lets Git open an unclassified store. The packed-refs parser skipped the header, so it accepted a header Git refuses and records out of the declared sorted order, where Git binary search cannot find the main ref. Against the current reader these tests fail: an object only in a transient alternate store still qualifies, and both packed-refs files qualify although Git itself cannot resolve refs/remotes/origin/main from them.
…3190 reader Review ToDo3505 found that Git still followed objects/info/alternates of the shared store, so a file placed while the reader ran and removed before the recheck could make Git read an unclassified store, and that packed-refs was parsed without its header and declared order. The reader no longer runs Git. It reads loose objects and version 2 pack indexes with their packs from the source store only, applies offset and named-base deltas within a pack, and uses an object only when its SHA-1 name matches. The guard classifies the pack directory, every index, pack and loose file first. Alternates, commit-graph, multi-pack-index and replace refs are never read. packed-refs accepts only the header and traits Git writes and requires strict order when the file declares sorted. Tests compare every object, commit, tree listing and ancestry with Git for loose objects, offset deltas, named-base deltas and a mixed store.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 99f3c699-2bb5-4045-a9d1-35321769cb3d) |
…nd its object reads Review ToDo3513 found three Musts. These tests reproduce each of them against the current reader: - A commit whose last line is a "parent " line without its newline has no parent to Git, but the reader read one, so a crafted tip of main made the fixed commit look like its ancestor. TestParseCommitAgreesWithGit compares parseCommit with git log for eleven commit shapes, and TestRefusesParentsGitDoesNotRead checks with git merge-base that Git finds no ancestry where the reader qualified the source. - The pack directory was listed by its path after it was pinned, so a junction put in its place listed names from a directory that the guard never classified. TestListsOnlyThePinnedPackDirectory swaps the directory right before the listing. - A delta was inflated before its base was read, so a chain that returns to itself held one inflated delta per step up to the depth limit: TestDeltaCyclesInflateNothing measures about 250 MB allocated for 16 KiB deltas. Nothing bounded the packs, the index bytes, the bytes read, the commits walked or the tree listed. TestDeltaDepthLimit, TestReadLimit, TestReadLimitCountsCompressedBytes and TestObjectLimits set each bound one below what the source needs. The profile gains the bounds, and the environment a hook that runs before the pack directory is listed, so that the tests compile. The reader does not use either yet.
…190 reader - parseCommit follows Git's parse_commit_buffer: the tree line needs more bytes after it, and a "parent " line is read only while more than its length follows, as "parent <id>\n". A shorter "parent " line, which Git reads as no parent, refuses the commit, so every commit that the reader accepts has the parents that Git reads. - The pack directory is listed through the handle that pinned it, with GetFileInformationByHandleEx, and never reopened by its path. A directory put in its place after the pin is never listed. - A pack entry is rebuilt in the order of Git's unpack_entry. The headers alone are followed down to a whole object, and a chain that returns to an entry or passes the depth limit is refused before anything is inflated. The base is then inflated and the deltas are applied from the innermost one up, each inflated only to apply it. - objectLimits in the profile bounds the packs (64), the pack directory's entries (4,096), the indexes' total (256 MiB), the bytes read, inflated and rebuilt (512 MiB), the delta chain (4,095, the most Git writes), the commits walked (65,536), the tree entries with subtrees (65,536) and the listed paths' total (16 MiB). Measured by the reader itself with the deployed guard, the real source needs 20 entries, 6 packs, 529,584 index bytes, 982,328 bytes read, 3 deltas, 45 commits, 1,657 entries and 53,698 path bytes. The tests now also tell the five read charges apart, allow at most 256 KiB to be allocated while a delta cycle is refused, and bound the indexes' total over two packs, so that removing any one of these checks fails a test.
The specification describes the commit parsing that follows Git, the pack directory listed through its pinned handle, the delta chain walked by its headers first, and the object limits with what the real source needs. It records review ToDo3513, the Red and Green runs, the 24 mutants, the pre-merge reader run on the real files and the CI of the previous head.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: b45d1dea-cc20-4c9e-9533-71d7e6ecf0e4) |
…keep one type per name Review 3531 found that the reader accepts a main commit whose tree line names the fixed commit. Git refuses it, because it cannot use one object as both a tree and a commit (object.c, object_as_type). Git's merge-base also reads every commit that main reaches and the fixed commit does not, with their parents, before it answers (commit-reach.c, paint_down_to_common); the reader stops at the fixed commit. TestRefusesNamesOfTwoTypes and TestReadsEveryCommitGitReads run git merge-base --is-ancestor on each history and require the reader to refuse the source where Git exits 128 and to qualify it where Git exits 0. The merges name each side branch before and after the fixed commit, so a walk that stops at the fixed commit misses it in whatever order it reads the parents. TestDeltaDepthCountsKeptBases requires the delta depth limit to count a chain that reaches a base kept by an earlier read. Red (go test -race, these three tests): six histories fail as intended, the reader qualifying where Git exits 128, and a chain of two deltas is read under a limit of one. A parent line that names the fixed tree is already refused, and a side branch of whole commits is qualified in both orders where Git exits 0. No data race, panic or build failure.
…Do3190 reader The reader now gives each object name one type, as Git's object table does: the type of each object that it reads, and the tree type for each commit's tree line. A name given a second type refuses the source, as Git refuses to use one object as two types (object.c, object_as_type). The walk from main reads every commit that main reaches, down to the shallow boundary, and no longer stops at the fixed commit. Before Git's merge-base answers, it reads every commit that main reaches and the fixed commit does not, with their parents (commit-reach.c, paint_down_to_common), so a commit there that Git cannot read, or that types a name twice, refuses the source here too. The walk no longer needs commit times, and parseCommit no longer reads the committer line. The delta depth limit now counts a chain that reaches a base kept by an earlier read: each kept object records the deltas of its own chain. go test -race -count=1 ./...: exit 0, 268 s beside the mutation runs; 43 top-level tests and 226 with subtests pass, with no failure, skip or data race. go vet and gofmt are clean. All 34 mutants are killed. The 10 new ones stop the walk at the fixed commit, follow first parents only, leave tree lines or reads untyped, never let types conflict, type a tree line as a commit, ignore the kept depth, keep it as zero, compare it one off, or walk past shallow boundaries. Measured once on the real source, with a measuring overlay through the installed guard's classification: 1,291 commits walked, 6,755,668 bytes read, delta chains of 3 at most and 2,663 typed names, all under the production limits.
…it Git reads The specification now describes the walk that reads every commit main reaches, the one type kept for each object name, and the delta depth counted through kept bases. It records review 3531, the fifth fix with its Red, Green and mutation results, the real source's needs measured after the fix, the pre-merge observation with the reader built from c2ff858, and the CI of the previous head b3c0b71.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: d757d5ae-595b-4b60-96b3-66b2d2a26d3e) |
…ixtures CI run 40040005613 failed in TestObjectsIgnoreAlternates: the fixture's loose commit 178adb4b... was gone before the test moved it. Git 2.54.0 starts maintenance after each commit, detached, and its default strategy (geometric) repacks the loose objects and removes their files as soon as objects/17 holds two of them: too_many_loose_objects in builtin/gc.c samples that one directory against ceil(100 / 256). A fixture whose commit name starts with 17 could thus lose its loose objects while its test read them. Locally, a repository with two loose objects under 17 lost that directory within 0.5 s of a plain commit and gained a pack; with -c maintenance.auto=false it kept them. Every Git command that the tests run through testGit and gitRaw now passes -c maintenance.auto=false. The reader itself is unchanged.
…pe, as Git does Review 3545 found that the reader keeps the shallow file's names only as boundaries. When Git first parses a commit it reads the shallow file and looks each name up as a commit (shallow.c, register_shallow), whether or not the history reaches it, so Git refuses a tree line or a directory of the fixed tree that names one of them. TestRefusesNamesOfTwoTypes gains a main commit whose tree line names a shallow boundary that main does not reach: Git's merge-base exits 128. The new TestRefusesShallowNamesOfTrees names a directory of the fixed tree in the shallow file: git ls-tree -r fails. Both require the reader to refuse the source. Red (go test -race, these two tests): both new cases fail as intended, the reader qualifying where Git refuses. The other three type cases pass. No data race, panic or build failure.
…as Git does When Git first parses a commit it reads the shallow file and looks each name up as a commit (shallow.c, register_shallow), whether or not the history reaches it. The reader now gives each name of the shallow file the commit type when it opens the store, so a tree line or a directory of the fixed tree that names one of them refuses the source, as Git refuses it. Git registers the names after it has looked up the first commit's tree and only reports a conflict with that tree, so a shallow file that names the fixed tree itself is refused by the reader alone. go test -race -count=1 ./...: exit 0, 255 s beside the mutation runs; 44 top-level tests and 228 with subtests pass, with no failure, skip or data race. go vet and gofmt are clean. All 35 mutants are killed, among them the new one that leaves shallow names untyped.
…he typed shallow names The specification now gives shallow names the commit type, as Git's register_shallow does, notes that a repack by automatic maintenance in the shared repository can refuse a run, which is then rerun, and explains why the tests keep Git's automatic maintenance off. It records CI run 40040005613, whose one failure came from that maintenance in a test fixture, review 3545, the sixth fix with its Red, Green and mutation results, and the pre-merge observation with the reader built from bb3bb1b.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 5668b7c6-fedb-4cf6-9d5c-26dd16789d2b) |
gitConfigList started Git with exec.Command directly, so it ran without -c maintenance.auto=false, unlike every other Git the tests start. Start it through gitCommand and keep the variables that stop Git from reading the system and global configuration. git config --file lists only that file, so the values TestConfigParserAgreesWithGit compares are unchanged. The reader itself is unchanged. Suggestion from the review in ToDo3548 (gpt-6.1-sol, max).
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 5425a101-5f57-4175-ab56-a672472f7c7f) |
内容
親ToDo3190が PR #376 の通常merge
0e16e817から生成した新しいtodo.exe候補(d4392e63…0718)を、現在の配布物と原生成記録へ結び付け、配布前の資格を読取専用で返す reader を追加します。共有guard(repair-guard)の source・profile・配布物は変更しません。tools/todo-update-reader-3190(配布しない検査用 reader)todo-update-reader-3190.exe --verify-todo-runtime-update D:/CC/Tools/todo/todo.exef8710b51…、新候補、固定source(HEAD0e16e817、tree31d45a74、入力126件)、原build receipt20345f44…、log、dep-info はすべて compiled pin で、caller から値を受け取りません。--guard-pathが clear を返した後で読みます。Gitの設定と参照(worktreeの登録、HEAD、branchとorigin/mainのref、repoのconfig、shallow)は reader 自身が読み、すぐ閉じます。configは厳密に解析し、include・includeIf・url の節、通常の非bare repoにないcoreのキー、worktreeConfig以外の拡張を拒否します。packed-refsはGitと同じに見出しとsortedを解釈し、宣言と並びが合わないファイルを拒否します。objects/packは、固定した handle からGetFileInformationByHandleExで列挙し、パスから開き直しません。unpack_entryと同じ順で組み立てます。見出しだけで基底までたどり、循環と深さ(4,095)の超過は何も展開せずに拒否します。cache に残した object に着いたときも、その連鎖の深さを足して数えます。同時に持つのは組み立て中の object・delta 1つ・その結果だけです。読取の量は profile のobjectLimitsで上限を置きます(pack 64組、項目4,096、index合計256MiB、読取byte 512MiB、commit 65,536件、treeのentry 65,536件、パス合計16MiB)。実物の必要量は、項目20、pack 6組、index 529,584 bytes、読取 6,755,668 bytes、deltaの深さ3、commit 1,291件、entry 1,657件、パス 53,698 bytes です。parse_commit_bufferと同じ長さと改行の判定で読み、Gitが親として読まない短いparentの行を持つ commit を拒否します。固定HEADの commit の tree が固定treeと一致すること、origin/mainから届く commit に固定HEADがあること、固定treeの一覧を求めます。origin/mainから届く commit は、固定HEADに着いた後も shallow の境界まで全部読みます。Git のmerge-base --is-ancestorは、答える前に、origin/mainから届いて固定HEADから届かない commit を全部とその親を読むので、そこで Git が拒否する履歴は reader も拒否します。Git と同じく名前ごとに型を1つだけ持ち(読んだ object の型、tree 行が指す名前の tree 型、shallow ファイルの名前の commit 型)、1つの名前に2つ目の型が付く履歴を拒否します。各入力は reader 自身が、固定したbytesのGit blob値を固定treeのblobと照合します。入力以外の追跡ファイルは分類も読取もしません。data_access_granted:falseです。.github/workflows/todo-update-reader-3190.yml(Windows self-hosted、go test -race・vet・build)。テストが起動する Git には-c maintenance.auto=falseを付け、合成 repo を書いたとおりに保ちます(Git 2.54.0 の自動 maintenance は、objects/17に loose object が2つあるだけで repack して loose のファイルを消すため)。docs/todo-update-reader-3190.md検証の状況
5b22ab24(go test -count=1 ./...終了1、検証系14テストが意図した理由で失敗)→ Green(race、上位18件・106件合格)。dd4b0273→ Green74c87e30(race、上位21件・113件合格)。6bda9f63(失敗48)→ Green2f804f13(race、上位27件・153件合格)。3b9b98d3(失敗6)→ Greenfe1d5941(race、上位32件・180件合格)。9fb6c2b5(失敗23)→ Green7a008ac9(race、上位40件・214件合格)。edbf9629(新しい3テストを race で実行し終了1、14秒。Gitのmerge-base --is-ancestorが終了128で拒否する6つの履歴で資格を得た。cache に残った delta を基底にする深さ2の連鎖を上限1で読んだ)→ Greenc2ff8584(go test -race -count=1 ./...終了0、268秒、上位43件・226件合格、skip 0、data race 0)。go vetと gofmt は差分なし。64b28eb2(新しい2つの形を race で実行し終了1。たどる範囲に無い shallow の名前を tree 行が指す履歴で Git のmerge-baseは終了128、固定treeの directory を shallow に載せるとls-tree -rは失敗したのに、どちらも資格を得た)→ Greenbb3bb1be(go test -race -count=1 ./...終了0、255秒、上位44件・228件合格、skip 0、data race 0)。テストの Git の maintenance を止める修正は59414fcd。3cd052b5で、設定の比較テストのgitConfigListもgitCommandから Git を起動するようにしました(go test -race -count=1 -run 'TestConfigParser|TestPackedRefs' ./...合格、vet・gofmt は差分なし)。reader 本体は変えていません。go test -overlayで当てました。最初の8版のうち7版、1回目の修正の3版、2回目の修正の11版、3回目の修正の18版、4回目の修正の24版、5回目の修正の34版、6回目の修正の35版で、対応テストが失敗しました。d9edfd4d、308cb24d、8b4f34b9、206181d4、b3c0b718、d30bc929(windows run 40042426529、CodeQL run 40042426578)で成功。4ed83158は CodeQL(run 40040005526)が成功し、windows(run 40040005613)がTestObjectsIgnoreAlternatesの1件で失敗しました。原因は reader ではなく、テストの合成 repo で commit の後に動いた Git の自動 maintenance が fixture の loose commit を repack で消したことで、59414fcdで直しました。提案を反映した head0a63ca86は実行中です(windows run 40045526436、CodeQL run 40045526497)。3cd052b5で反映しました。bb3bb1beから生成したreader(SHA256a148a4c9…525e)でqualified:true、入力126件・dep-info 116件・crate 8件、data_access_granted:falseを確認しています。PR下部の自動要約(Cursor)は参考です。検証の状況は上の区分を正とします。
対象外
todo.exeの置換・配布、ToDo 3050/3084/3110/3190 の状態変更、本人回答の記録と質問の終了は親3190が担当します。Rust の再ビルドはしていません。Refs #408、#325