Extend character manifest to external sources

This commit is contained in:
732642856
2026-07-02 17:13:17 +08:00
parent b50161a8ce
commit ac2cd9776f
9 changed files with 13732 additions and 42 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
# External High-Relevance Inventory Manifest
- status: pass
- scope: external
- generated_at: 2026-07-02T09:12:09.488794+00:00
- total_files: 883
- unhashed_files: 0
- unclassified_files: 0
- unknown_extraction_status: 0
- Heavy OCR: disabled
- Whole-machine scan: disabled
## Root Summary
| Root | Files | Bytes |
| --- | ---: | ---: |
| `~/.workbuddy` | 767 | 40507359 |
| `~/Desktop` | 5 | 1616896 |
| `~/Documents/ObsidianVault` | 11 | 45553 |
| `~/Downloads` | 15 | 5701907 |
| `~/WorkBuddy` | 36 | 665806 |
| `~/engines-repo` | 6 | 53358668 |
| `~/文件仓库` | 43 | 161766552 |
## Extraction Status Counts
- `binary_indexed`: 83
- `document_text_extraction_queued`: 11
- `image_ocr_queued`: 50
- `pdf_text_extraction_queued`: 2
- `text_decode_lossy`: 2
- `text_indexed`: 735
## Classification Counts
- `external_archive_or_binary`: 23
- `external_book_or_document`: 46
- `external_engine_fragment`: 10
- `external_historical_report`: 37
- `external_skill_fragment`: 767
## Boundary
External scan is high-relevance only. It records hashes and extraction states without copying private source text into repo truth.
This manifest proves indexing, hashing, and extraction-state classification. It does not by itself promote any source into the runtime truth chain.
@@ -0,0 +1,758 @@
{
"scope": "extraction-queue",
"status": "pass",
"generated_at": "2026-07-02T09:12:17.049464+00:00",
"mode": {
"heavy_ocr": false,
"whole_machine_scan": false,
"external_high_relevance_scan": true,
"semantic_promotion": false,
"boundary": "Queue only. It identifies PDF/image/document extraction work without performing OCR or promoting extracted text."
},
"summary": {
"queued_files": 66,
"unhashed_files": 0,
"source_counts": {
"external": 63,
"project": 3
}
},
"queue_counts": {
"document_text_extraction_queued": 11,
"image_ocr_queued": 53,
"pdf_text_extraction_queued": 2
},
"queue": [
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Desktop/一楠印度占星 Level 3 综合解盘.docx",
"suffix": ".docx",
"byte_count": 19165,
"sha256": "cee1d2837d7e1d396fe3b6ba06012e16e0dad3d2d68554e5116d25789170b64b",
"extraction_status": "document_text_extraction_queued",
"classification": "external_historical_report",
"priority": "priority_2",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Desktop/印度占星修正版研究结论v3-高压基建后的反转兑现模型.docx",
"suffix": ".docx",
"byte_count": 21966,
"sha256": "b8d1b8c9bae94d7b1102db321543ef34cf4d4b5d7b822c3365e059903be40fb4",
"extraction_status": "document_text_extraction_queued",
"classification": "external_historical_report",
"priority": "priority_2",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/jyotish_training.agent.final.docx",
"suffix": ".docx",
"byte_count": 186879,
"sha256": "a0d83aa8a70f9966cbe79945c4416906e1382c9093998c23d73bd395b02f23fe",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/WorkBuddy/2026-06-09-20-03-34/jyotish-fragments/training/jyotish_training.agent.final.docx",
"suffix": ".docx",
"byte_count": 186879,
"sha256": "a0d83aa8a70f9966cbe79945c4416906e1382c9093998c23d73bd395b02f23fe",
"extraction_status": "document_text_extraction_queued",
"classification": "external_historical_report",
"priority": "priority_2",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/1印度占星.docx",
"suffix": ".docx",
"byte_count": 6142110,
"sha256": "210a09d764f8fe17ded6c908d5073648908892a99ae853f983e6f516ccc0164d",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/2印度占星.docx",
"suffix": ".docx",
"byte_count": 3031411,
"sha256": "16979422e7621475243e2739fda00767d1a9dc367f7f10153fe36d8af6e15c6d",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/3印度占星.docx",
"suffix": ".docx",
"byte_count": 2149772,
"sha256": "59ee274246547f4afb581ec26f5a4aad03ba2095309d66c40a60a51d1244df92",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/4印度占星.docx",
"suffix": ".docx",
"byte_count": 5572637,
"sha256": "e789430dc1df6ab7054a5b28f0665da325e7c5eb2e6daac39f1f17ac29ba7629",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/Tajika占星术是印度占星术体系之一.docx",
"suffix": ".docx",
"byte_count": 1338151,
"sha256": "254624594c26fc80ad5bfa9e9a880ac0cd81c9388b19721d5767dfa5c55e953c",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星Jaimini系统的Darakaraka的理解和说明如何解释.docx",
"suffix": ".docx",
"byte_count": 44926,
"sha256": "f2db03d1c0392e2cdad66da9a731117c0ddf290cad7712b9c3de0d8234a17a29",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/高地位配偶与婚后命运积极转变的占星规则与组合.docx",
"suffix": ".docx",
"byte_count": 1120887,
"sha256": "9cdd6996670ad46063c9582957c6d98269e069f96801ff340ce0c84de325d412",
"extraction_status": "document_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/.workbuddy/skills/jyotish-vedic-astrology/assets/jyotish-logo.png",
"suffix": ".png",
"byte_count": 1496430,
"sha256": "4cb571aa402da3a730a73b6594ec88adf90f2d807018e23f96fd1bee426c23ed",
"extraction_status": "image_ocr_queued",
"classification": "external_skill_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/.workbuddy/skills/jyotish-vedic-astrology/references/open_source_sources/VedicAstro/VedicAstroLogo.png",
"suffix": ".png",
"byte_count": 151345,
"sha256": "e64022e2dde10d7ce378a69f8b7f035b084d56517f37eb8f6ecc4bd47fe0fe2a",
"extraction_status": "image_ocr_queued",
"classification": "external_skill_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/.workbuddy/skills/jyotish-vedic-astrology/references/open_source_sources/vedic-astro-skills/assets/alipay.jpg",
"suffix": ".jpg",
"byte_count": 164821,
"sha256": "f7d4dcf08239c738d5cd45d531401bdef92e4c1b7ea93965b7a7f51ab76dc8fc",
"extraction_status": "image_ocr_queued",
"classification": "external_skill_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/.workbuddy/skills/jyotish-vedic-astrology/references/open_source_sources/vedic-astro-skills/assets/wechat.jpg",
"suffix": ".jpg",
"byte_count": 71804,
"sha256": "e94c027499059e7fb2170474fb03e3be875b4251dfbea5fc7f39b370d6242a62",
"extraction_status": "image_ocr_queued",
"classification": "external_skill_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Desktop/电脑相册/重复图片移动前备份清单_2026-05-20/file_backups/WorkBuddy/yinduzhanxing/assets/jyotish-logo.png",
"suffix": ".png",
"byte_count": 1496430,
"sha256": "4cb571aa402da3a730a73b6594ec88adf90f2d807018e23f96fd1bee426c23ed",
"extraction_status": "image_ocr_queued",
"classification": "external_historical_report",
"priority": "priority_2",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3004.jpg",
"suffix": ".jpg",
"byte_count": 335978,
"sha256": "3e87432635d0b7d6181a5d251191ab397352bfc0908bedef4d07b135aa351012",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3005.jpg",
"suffix": ".jpg",
"byte_count": 365592,
"sha256": "3743a89d6611dd8a3a344db2e9ca6877b6f1f610b48bd3cbb9486eb8a81f4195",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3006.jpg",
"suffix": ".jpg",
"byte_count": 387356,
"sha256": "03689b8775950267002cbbf9b96bf5038217c692c311188429ee99903beea285",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3007.jpg",
"suffix": ".jpg",
"byte_count": 457489,
"sha256": "d959e3b1625ec6d7d0a3129f8fd856f18b06de2d4b874bbc3322e1c9818b5aa3",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3008.jpg",
"suffix": ".jpg",
"byte_count": 447919,
"sha256": "8999a66852d45b5e89b53823bff4aecb5b3fca935495572ee3422c1a067d2507",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3009.jpg",
"suffix": ".jpg",
"byte_count": 436786,
"sha256": "e4a1eaa1730a22d96e4032bf405e0ed556395a0d9a27a2c5ec7265b8a3c3fdf8",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3010.jpg",
"suffix": ".jpg",
"byte_count": 352127,
"sha256": "b6478c3db82ad67534fae2bcd52e29da9bb1ffa573271097e8449cfbf1be5537",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3011.jpg",
"suffix": ".jpg",
"byte_count": 347111,
"sha256": "0d786f1284ff916f37055c77101559955e83eecba24b290eb80ab808e7a90ce4",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3012.jpg",
"suffix": ".jpg",
"byte_count": 282794,
"sha256": "319b2ab6948d82e04eaf957caf5310c85f9c42db66f2c39f8e27bcca7528487a",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3013.jpg",
"suffix": ".jpg",
"byte_count": 296069,
"sha256": "2253eebdc5a3f7b26400cffd3b0de32b33b29896783406b85a537957059c3115",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星11页/IMG_3014.jpg",
"suffix": ".jpg",
"byte_count": 288473,
"sha256": "5d7e8abcd4cdb922d0da0fa987fd4264b57339230586b38570a360f2b1f6f1cd",
"extraction_status": "image_ocr_queued",
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3223.JPG",
"suffix": ".jpg",
"byte_count": 6709612,
"sha256": "8a2f44bc4c57b8d352371101a662305b88d4f834d385c22a6f0b308c66119890",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3224.JPG",
"suffix": ".jpg",
"byte_count": 4388817,
"sha256": "30b7f2ea6bcc962ccb3064f3656e7d298a5469e084d1fdf21322bc5ea3b21d90",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3225.JPG",
"suffix": ".jpg",
"byte_count": 3539980,
"sha256": "4edcfa12004b48047dbff7f5512914aedd40768b37dc75ad45c467c05802ab25",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3226.JPG",
"suffix": ".jpg",
"byte_count": 3118339,
"sha256": "820f1c29b11e09ed51681be292adf15919ca3d5157b23fb29d098023f4423c52",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3227.JPG",
"suffix": ".jpg",
"byte_count": 6945195,
"sha256": "c6643554be26edc62274f8b4e0acd1cf305aaeff8c1720cb5b731e0842fada46",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3228.JPG",
"suffix": ".jpg",
"byte_count": 2188130,
"sha256": "d118ce2ed91a3a3b3551833f20cca6604011d22ff921976d50e5cd21d08db3e7",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3229.JPG",
"suffix": ".jpg",
"byte_count": 1390060,
"sha256": "630e6f8f91bfda5fcb3871c30758dec2db32108b89ec511d25aebea7313f0cfe",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3230.JPG",
"suffix": ".jpg",
"byte_count": 2723452,
"sha256": "be3a1ddb38f42961273827537ce8a8c73dd2c5ad5e1effd90c49bcf623b196c2",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3231.JPG",
"suffix": ".jpg",
"byte_count": 3460758,
"sha256": "279b99af4faa614eb21ef8d2a2bc67bf87a4cce112ba04627bca67f2f343297b",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/占星印度1/IMG_3232.JPG",
"suffix": ".jpg",
"byte_count": 3256088,
"sha256": "a46fb026076a3314ef9f263a7234659c7d2b75068aba20271d07d0f7a3b674b1",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3233.JPG",
"suffix": ".jpg",
"byte_count": 5620475,
"sha256": "c3ed9e4d29163d9baa0f8b95669cfcbb52f20727ce23f105c5fc8c688cbd5fb6",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3234.JPG",
"suffix": ".jpg",
"byte_count": 4739555,
"sha256": "1833b90c725ae183f3acf28bc7738858f7750e4dff67bd7157a70fb168c3301a",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3235.JPG",
"suffix": ".jpg",
"byte_count": 5869585,
"sha256": "13a85b875309b89a47cfe13e52998cf85b1e5da09cc0905ac967877bb0f00091",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3236.JPG",
"suffix": ".jpg",
"byte_count": 8733982,
"sha256": "6cd213236d66c235c8fe54722411375144e988029ef959a9e051f186399bbbc6",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3237.JPG",
"suffix": ".jpg",
"byte_count": 5434137,
"sha256": "8a3eda7d1936530abb1ed5c93040b316bd013f02155d2b20eb9626bd84e7480e",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3238.JPG",
"suffix": ".jpg",
"byte_count": 5918325,
"sha256": "c29063134357d4c7cb081d14c35c5558a47685913eff21be4a229703edd7256b",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3239.JPG",
"suffix": ".jpg",
"byte_count": 5110208,
"sha256": "6633e0045710c1f6defda6624ec7c8ccf1c22dcb3bc34bf41bba7f48153a3255",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3240.JPG",
"suffix": ".jpg",
"byte_count": 4155054,
"sha256": "5003bc196b649a9ee6f2cf1583ca55f13cc695030571184c5bf6db429775a5a6",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3241.JPG",
"suffix": ".jpg",
"byte_count": 1262218,
"sha256": "4ffe9f3aadd4485ba99382a84d54abb02ae2520d8b0f57147fa7438408ce39d4",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3242.JPG",
"suffix": ".jpg",
"byte_count": 1867271,
"sha256": "a15fadbbd4a250a30bac8e27915210b67fa6dc2ceb482b6a3792a0acba87bd77",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3243.JPG",
"suffix": ".jpg",
"byte_count": 6305912,
"sha256": "64bd2a3a548a2c94372c1e3312e9f87bd291a276e73a3918fcc903e41f0fea9f",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3244.JPG",
"suffix": ".jpg",
"byte_count": 7177473,
"sha256": "e7a486925218295da284ca6ca167ae92f2a37285e26ea5161094f531cd12ebd4",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3245.JPG",
"suffix": ".jpg",
"byte_count": 6429277,
"sha256": "c5fd2b815ca6d295b8de5402084a9a7cdd3f9d2f08e53c50abd45b1352eb8b06",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3246.JPG",
"suffix": ".jpg",
"byte_count": 92516,
"sha256": "355ae43a243fcdea344500b1b3f53a47683050cd73b01428fa9490844d04e222",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3247.JPG",
"suffix": ".jpg",
"byte_count": 7406196,
"sha256": "0bc4a95e8d9ad41e6df47cd80a7cf84b2df24cb5400b9de56d63d3be66818e7f",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3248.JPG",
"suffix": ".jpg",
"byte_count": 3862462,
"sha256": "a3b3f89232cece9d3475cd48a3fff7878a62233743927fa2492b2bb646dfefa3",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3249.JPG",
"suffix": ".jpg",
"byte_count": 7780975,
"sha256": "8ae9bd3960a150e88feb78813d245c73aa2e6473ff16182a61632241f9f50014",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/印度占星查悦/IMG_3250.JPG",
"suffix": ".jpg",
"byte_count": 7389728,
"sha256": "6c895f9ddd16c2a1edf785858203cbec1a86c1abb3c6b24e03d24e74d1ea175d",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3216.JPG",
"suffix": ".jpg",
"byte_count": 1441554,
"sha256": "3cdbdd47967592f2581b820b9df49aecd7117b377c45c71571b440cce3f5dddd",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3218.JPG",
"suffix": ".jpg",
"byte_count": 1968582,
"sha256": "58d9de5cd5399e74e007d7d50f093dc50d7d0688df484a26646787a07a0c7e92",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3219.JPG",
"suffix": ".jpg",
"byte_count": 2710815,
"sha256": "47bedfc7e2fdc225305afc31cc4ab74bd0021bd65e61b9d85e1b0909aa5950b6",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3220.JPG",
"suffix": ".jpg",
"byte_count": 741635,
"sha256": "771256b16a47d9db785abbeab01b11388900e7e98d45220ff0defb52f24e7b1b",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3221.JPG",
"suffix": ".jpg",
"byte_count": 1275215,
"sha256": "b219ca2e664ddff55ef95681aeaf5362c3a1589b2bef562129e39e8960e0202e",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/文件仓库/印度占星文章/受孕/IMG_3222.JPG",
"suffix": ".jpg",
"byte_count": 1340781,
"sha256": "a7601e255c4d398d2480dc71fb99916aa67c56f978a722943970806f805bde26",
"extraction_status": "image_ocr_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "project",
"path": "references/open_source_sources/VedicAstro/VedicAstroLogo.png",
"suffix": ".png",
"byte_count": 151345,
"sha256": "e64022e2dde10d7ce378a69f8b7f035b084d56517f37eb8f6ecc4bd47fe0fe2a",
"extraction_status": "image_ocr_queued",
"classification": "open_source_reference",
"priority": "priority_2",
"promotion_status": "reference_layer_candidate"
},
{
"source_scope": "project",
"path": "references/open_source_sources/vedic-astro-skills/assets/alipay.jpg",
"suffix": ".jpg",
"byte_count": 164821,
"sha256": "f7d4dcf08239c738d5cd45d531401bdef92e4c1b7ea93965b7a7f51ab76dc8fc",
"extraction_status": "image_ocr_queued",
"classification": "open_source_reference",
"priority": "priority_1",
"promotion_status": "reference_layer_candidate"
},
{
"source_scope": "project",
"path": "references/open_source_sources/vedic-astro-skills/assets/wechat.jpg",
"suffix": ".jpg",
"byte_count": 71804,
"sha256": "e94c027499059e7fb2170474fb03e3be875b4251dfbea5fc7f39b370d6242a62",
"extraction_status": "image_ocr_queued",
"classification": "open_source_reference",
"priority": "priority_1",
"promotion_status": "reference_layer_candidate"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星.pdf",
"suffix": ".pdf",
"byte_count": 135905,
"sha256": "121507cf1d0d66711bc30c18f589f36f2d3cce797ecb13cdf4427625e6ec8894",
"extraction_status": "pdf_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
},
{
"source_scope": "external",
"path": "/Users/wuyongnaren/Downloads/印度占星1.pdf",
"suffix": ".pdf",
"byte_count": 132486,
"sha256": "92c9288c5d0011a869c545e3459f037ff09cd20f7508543bc0eb11b03079d930",
"extraction_status": "pdf_text_extraction_queued",
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only"
}
],
"artifacts": {
"json_report": "docs/research/character_level_extraction_queue_latest.json",
"markdown_report": "docs/research/character_level_extraction_queue_latest.md"
},
"title": "Extraction Queue Manifest"
}
@@ -0,0 +1,26 @@
# Extraction Queue Manifest
- status: pass
- scope: extraction-queue
- generated_at: 2026-07-02T09:12:17.049464+00:00
- queued_files: 66
- unhashed_files: 0
- Heavy OCR: disabled
- Whole-machine scan: disabled
## Queue Counts
- `document_text_extraction_queued`: 11
- `image_ocr_queued`: 53
- `pdf_text_extraction_queued`: 2
## Source Counts
- `external`: 63
- `project`: 3
## Boundary
Queue only. It identifies PDF/image/document extraction work without performing OCR or promoting extracted text.
Queued files are indexed by path, size, hash, and extraction status only. Private source text is not copied into this report.
@@ -1,10 +1,11 @@
{
"scope": "project",
"status": "pass",
"generated_at": "2026-07-02T08:50:52.465261+00:00",
"generated_at": "2026-07-02T09:11:51.839696+00:00",
"mode": {
"heavy_ocr": false,
"whole_machine_scan": false,
"external_high_relevance_scan": false,
"semantic_promotion": false,
"boundary": "Fast manifest only: hashes and cheap text stats now; heavy OCR and whole-machine scan are queued."
},
@@ -16,7 +17,7 @@
"AGENTS.md"
],
"summary": {
"total_files": 1069,
"total_files": 1073,
"unhashed_files": 0,
"unclassified_files": 0,
"unknown_extraction_status": 0,
@@ -27,12 +28,12 @@
"quarantined_draft": 93,
"real_case_calibration": 5,
"reference_candidate": 180,
"research_governance": 439
"research_governance": 443
},
"extraction_status_counts": {
"binary_indexed": 34,
"image_ocr_queued": 3,
"text_indexed": 1032
"text_indexed": 1036
}
},
"root_summary": {
@@ -45,8 +46,8 @@
"bytes": 43187
},
"docs/research": {
"files": 532,
"bytes": 2198779
"files": 536,
"bytes": 2901473
},
"references": {
"files": 236,
@@ -5168,14 +5169,70 @@
"promotion_status": "governance_or_history",
"reason": "Research history or governance record; index separately from rule truth."
},
"docs/research/character_level_external_manifest_latest.json": {
"path": "docs/research/character_level_external_manifest_latest.json",
"suffix": ".json",
"byte_count": 666472,
"sha256": "244465beREDACTED_TIMEe81a2c8b3abac4f41e28bd55ce9f42f3f203d6574c52617dcfd9",
"extraction_status": "text_indexed",
"character_count": 661666,
"line_count": 12446,
"decode_replacement_count": 0,
"classification": "research_governance",
"priority": "priority_3",
"promotion_status": "governance_or_history",
"reason": "Research history or governance record; index separately from rule truth."
},
"docs/research/character_level_external_manifest_latest.md": {
"path": "docs/research/character_level_external_manifest_latest.md",
"suffix": ".md",
"byte_count": 1280,
"sha256": "71cc5b027ea056c23ce1d5d1cda8921eeade065a8cb003910ea44d2a1165e642",
"extraction_status": "text_indexed",
"character_count": 1272,
"line_count": 47,
"decode_replacement_count": 0,
"classification": "research_governance",
"priority": "priority_3",
"promotion_status": "governance_or_history",
"reason": "Research history or governance record; index separately from rule truth."
},
"docs/research/character_level_extraction_queue_latest.json": {
"path": "docs/research/character_level_extraction_queue_latest.json",
"suffix": ".json",
"byte_count": 31649,
"sha256": "cbfc03eee31d16cc3c796728cf34504c6eaa0722c5e42dd2f7d2ec1da966c5ef",
"extraction_status": "text_indexed",
"character_count": 30151,
"line_count": 758,
"decode_replacement_count": 0,
"classification": "research_governance",
"priority": "priority_3",
"promotion_status": "governance_or_history",
"reason": "Research history or governance record; index separately from rule truth."
},
"docs/research/character_level_extraction_queue_latest.md": {
"path": "docs/research/character_level_extraction_queue_latest.md",
"suffix": ".md",
"byte_count": 633,
"sha256": "6a45bb22f9f44d248c6e8ff71d74949180eb3eba537cb42080d15fef88395725",
"extraction_status": "text_indexed",
"character_count": 633,
"line_count": 27,
"decode_replacement_count": 0,
"classification": "research_governance",
"priority": "priority_3",
"promotion_status": "governance_or_history",
"reason": "Research history or governance record; index separately from rule truth."
},
"docs/research/character_level_inventory_manifest_latest.json": {
"path": "docs/research/character_level_inventory_manifest_latest.json",
"suffix": ".json",
"byte_count": 719007,
"sha256": "b838192193044fda5ff7684778e26006c53be9892b9fafa70d7398641fbc2eed",
"byte_count": 721667,
"sha256": "aba3169d10fe86f02dfed264b150afb84473994225b2a62a36b720f485ad1a41",
"extraction_status": "text_indexed",
"character_count": 718457,
"line_count": 15032,
"character_count": 721117,
"line_count": 15090,
"decode_replacement_count": 0,
"classification": "research_governance",
"priority": "priority_3",
@@ -5186,7 +5243,7 @@
"path": "docs/research/character_level_inventory_manifest_latest.md",
"suffix": ".md",
"byte_count": 1090,
"sha256": "19fb031f612b0747384eb3801abc1d19088a233fb662dde690817b5d686aa342",
"sha256": "d088bb6312805a0f78506af37c2d32098e97af635eb3eed707a4943b75dae2ae",
"extraction_status": "text_indexed",
"character_count": 1090,
"line_count": 44,
@@ -15028,5 +15085,6 @@
"artifacts": {
"json_report": "docs/research/character_level_inventory_manifest_latest.json",
"markdown_report": "docs/research/character_level_inventory_manifest_latest.md"
}
},
"title": "Character-Level Inventory Manifest"
}
@@ -2,8 +2,8 @@
- status: pass
- scope: project
- generated_at: 2026-07-02T08:50:52.465261+00:00
- total_files: 1069
- generated_at: 2026-07-02T09:11:51.839696+00:00
- total_files: 1073
- unhashed_files: 0
- unclassified_files: 0
- unknown_extraction_status: 0
@@ -16,7 +16,7 @@
| --- | ---: | ---: |
| `AGENTS.md` | 1 | 3932 |
| `SKILL.md` | 1 | 43187 |
| `docs/research` | 532 | 2198779 |
| `docs/research` | 536 | 2901473 |
| `references` | 236 | 4008411 |
| `references/open_source_sources` | 299 | 9365015 |
@@ -24,7 +24,7 @@
- `binary_indexed`: 34
- `image_ocr_queued`: 3
- `text_indexed`: 1032
- `text_indexed`: 1036
## Classification Counts
@@ -34,7 +34,7 @@
- `quarantined_draft`: 93
- `real_case_calibration`: 5
- `reference_candidate`: 180
- `research_governance`: 439
- `research_governance`: 443
## Boundary
+15
View File
@@ -805,3 +805,18 @@
- 当前分类计数:`open_source_reference=299`、`reference_candidate=180`、`research_governance=439`、`quarantined_draft=93`、`real_case_calibration=5`、`oracle_artifact=51`、`project_governance=2`。
- 当前提取状态:`text_indexed=1032`、`binary_indexed=34`、`image_ocr_queued=3`;本轮为省算力模式,未跑重 OCR,也未跑全机器长扫描。
- 新增 `tests/test_character_level_inventory_manifest.py`,并把 manifest gate 接入 `scripts/run_quality_gate.py`;质量门使用 `--no-write --summary-only`,避免 CI 反复刷新报告或打印全量 `by_path`。
## 2026-07-02T17:08:00+08:00 - 外部高相关资料 manifest 第一阶段
- 扩展 `scripts/character_level_inventory_manifest.py --scope external`,只扫高相关外部路径,不做全机器深扫,不复制私人正文进仓库。
- 新增 `docs/research/character_level_external_manifest_latest.json` 与 `.md`,当前覆盖 `883` 个外部高相关文件:`unhashed_files=0`、`unclassified_files=0`、`unknown_extraction_status=0`。
- 外部来源桶:`~/.workbuddy=767`、`~/文件仓库=43`、`~/WorkBuddy=36`、`~/Downloads=15`、`~/Documents/ObsidianVault=11`、`~/engines-repo=6`、`~/Desktop=5`。
- 外部提取状态:`text_indexed=735`、`text_decode_lossy=2`、`pdf_text_extraction_queued=2`、`document_text_extraction_queued=11`、`image_ocr_queued=50`、`binary_indexed=83`。
- 外部分级:`external_skill_fragment=767`、`external_book_or_document=46`、`external_historical_report=37`、`external_engine_fragment=10`、`external_archive_or_binary=23`;均为 index/reference 阶段,不进入 runtime truth chain。
## 2026-07-02T17:11:00+08:00 - PDF/图片/文档提取队列 manifest
- 新增 `scripts/character_level_inventory_manifest.py --scope extraction-queue`,把项目内与外部 manifest 中的 PDF / 图片 / Office 文档待提取项合并为单独队列。
- 新增 `docs/research/character_level_extraction_queue_latest.json` 与 `.md`,当前 queued_files=`66`、`unhashed_files=0`。
- 队列构成:`image_ocr_queued=53`、`document_text_extraction_queued=11`、`pdf_text_extraction_queued=2`;来源为 `external=63`、`project=3`。
- 边界:本轮只做提取队列,不跑重 OCR,不把外部私人资料正文写进仓库,也不升格为 runtime truth。
+289 -25
View File
@@ -20,6 +20,10 @@ from typing import Any
ROOT = Path(__file__).resolve().parents[1]
REPORT_JSON = ROOT / "docs" / "research" / "character_level_inventory_manifest_latest.json"
REPORT_MD = ROOT / "docs" / "research" / "character_level_inventory_manifest_latest.md"
EXTERNAL_REPORT_JSON = ROOT / "docs" / "research" / "character_level_external_manifest_latest.json"
EXTERNAL_REPORT_MD = ROOT / "docs" / "research" / "character_level_external_manifest_latest.md"
EXTRACTION_QUEUE_JSON = ROOT / "docs" / "research" / "character_level_extraction_queue_latest.json"
EXTRACTION_QUEUE_MD = ROOT / "docs" / "research" / "character_level_extraction_queue_latest.md"
PROJECT_SCAN_ROOTS = [
"references",
@@ -29,6 +33,36 @@ PROJECT_SCAN_ROOTS = [
"AGENTS.md",
]
EXTERNAL_SCAN_ROOTS = [
Path.home() / "Downloads",
Path.home() / "Desktop",
Path.home() / "文件仓库" / "印度占星文章",
Path.home() / ".workbuddy" / "skills" / "jyotish-vedic-astrology",
Path.home() / "WorkBuddy" / "2026-06-09-20-03-34" / "jyotish-fragments",
Path.home() / "WorkBuddy" / "2026-06-10-21-30-47",
Path.home() / "WorkBuddy" / "2026-06-12-15-22-12",
Path.home() / "WorkBuddy" / "20260422235041",
Path.home() / "WorkBuddy" / "20260503121822",
Path.home() / "WorkBuddy" / "engines-repo" / "jyotish",
Path.home() / "engines-repo" / "jyotish",
Path.home() / "Documents" / "ObsidianVault" / "03_研究_术数占星",
]
EXTERNAL_KEYWORDS = [
"印度占星",
"占星",
"jyotish",
"vedic",
"astrology",
"jhora",
"pyjhora",
"vedastro",
"dasha",
"shadbala",
"tajika",
"jaimini",
]
EXCLUDED_PARTS = {
".git",
".pytest_cache",
@@ -65,6 +99,11 @@ TEXT_EXTENSIONS = {
PDF_EXTENSIONS = {".pdf"}
IMAGE_EXTENSIONS = {".bmp", ".gif", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp"}
DOCUMENT_EXTENSIONS = {".doc", ".docx", ".epub", ".mobi", ".pages", ".ppt", ".pptx", ".rtf", ".xls", ".xlsx"}
QUEUED_EXTRACTION_STATUSES = {
"pdf_text_extraction_queued",
"image_ocr_queued",
"document_text_extraction_queued",
}
def _relative(path: Path) -> str:
@@ -92,6 +131,26 @@ def _iter_project_files() -> list[Path]:
return sorted(files)
def _is_external_relevant(path: Path) -> bool:
lowered = str(path).lower()
return any(keyword.lower() in lowered for keyword in EXTERNAL_KEYWORDS)
def _iter_external_files() -> list[Path]:
files: set[Path] = set()
for root in EXTERNAL_SCAN_ROOTS:
if not root.exists() or _should_skip(root):
continue
if root.is_file():
if _is_external_relevant(root):
files.add(root)
continue
for path in root.rglob("*"):
if path.is_file() and not _should_skip(path) and _is_external_relevant(path):
files.add(path)
return sorted(files)
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
@@ -220,10 +279,74 @@ def _root_bucket(path: str) -> str:
return path.split("/", 1)[0]
def build_manifest(*, scope: str = "project", write: bool = True) -> dict[str, Any]:
if scope != "project":
raise ValueError("Only --scope project is supported by the lightweight manifest.")
def _external_path(path: Path) -> str:
return str(path)
def _classify_external_path(path: str) -> dict[str, str]:
suffix = Path(path).suffix.lower()
if "/.workbuddy/skills/jyotish-vedic-astrology/" in path:
return {
"classification": "external_skill_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate",
"reason": "External Jyotish skill copy; index only until diffed against repo truth.",
}
if "/engines-repo/jyotish/" in path or "/WorkBuddy/engines-repo/jyotish/" in path:
return {
"classification": "external_engine_fragment",
"priority": "priority_1",
"promotion_status": "external_candidate",
"reason": "External engine fragment; requires source diff and license boundary review.",
}
if "/WorkBuddy/" in path or "/Desktop/" in path:
return {
"classification": "external_historical_report",
"priority": "priority_2",
"promotion_status": "external_reference_only",
"reason": "Historical report or work artifact; do not promote without privacy review.",
}
if suffix in PDF_EXTENSIONS or suffix in DOCUMENT_EXTENSIONS or "/文件仓库/印度占星文章/" in path:
return {
"classification": "external_book_or_document",
"priority": "priority_1",
"promotion_status": "external_reference_only",
"reason": "External book/document source; requires extraction and source grading before use.",
}
return {
"classification": "external_archive_or_binary",
"priority": "priority_3",
"promotion_status": "external_index_only",
"reason": "External relevant asset; indexed without content promotion.",
}
def _external_root_bucket(path: str) -> str:
home = str(Path.home())
if path.startswith(home + "/Downloads/"):
return "~/Downloads"
if path.startswith(home + "/Desktop/"):
return "~/Desktop"
if path.startswith(home + "/文件仓库/"):
return "~/文件仓库"
if path.startswith(home + "/.workbuddy/"):
return "~/.workbuddy"
if path.startswith(home + "/WorkBuddy/"):
return "~/WorkBuddy"
if path.startswith(home + "/engines-repo/"):
return "~/engines-repo"
if path.startswith(home + "/Documents/ObsidianVault/"):
return "~/Documents/ObsidianVault"
return path
def _build_index(
*,
files: list[Path],
path_label,
classify,
root_bucket,
) -> tuple[dict[str, dict[str, Any]], dict[str, Any], dict[str, dict[str, int]]]:
by_path: dict[str, dict[str, Any]] = {}
classification_counts: dict[str, int] = {}
extraction_counts: dict[str, int] = {}
@@ -231,9 +354,9 @@ def build_manifest(*, scope: str = "project", write: bool = True) -> dict[str, A
unclassified_files = 0
unknown_extraction_status = 0
for file_path in _iter_project_files():
rel = _relative(file_path)
file_class = _classify_path(rel)
for file_path in files:
rel = path_label(file_path)
file_class = classify(rel)
extraction = _extraction_status(file_path)
if not file_class.get("classification"):
unclassified_files += 1
@@ -251,7 +374,7 @@ def build_manifest(*, scope: str = "project", write: bool = True) -> dict[str, A
by_path[rel] = item
classification_counts[item["classification"]] = classification_counts.get(item["classification"], 0) + 1
extraction_counts[item["extraction_status"]] = extraction_counts.get(item["extraction_status"], 0) + 1
bucket = _root_bucket(rel)
bucket = root_bucket(rel)
root_summary.setdefault(bucket, {"files": 0, "bytes": 0})
root_summary[bucket]["files"] += 1
root_summary[bucket]["bytes"] += byte_count
@@ -264,41 +387,148 @@ def build_manifest(*, scope: str = "project", write: bool = True) -> dict[str, A
"classification_counts": dict(sorted(classification_counts.items())),
"extraction_status_counts": dict(sorted(extraction_counts.items())),
}
status = "pass" if summary["unhashed_files"] == 0 and unclassified_files == 0 and unknown_extraction_status == 0 else "fail"
return by_path, summary, dict(sorted(root_summary.items()))
def build_manifest(*, scope: str = "project", write: bool = True) -> dict[str, Any]:
if scope == "project":
files = _iter_project_files()
path_label = _relative
classify = _classify_path
root_bucket = _root_bucket
scan_roots = PROJECT_SCAN_ROOTS
json_report = REPORT_JSON
markdown_report = REPORT_MD
title = "Character-Level Inventory Manifest"
mode = {
"heavy_ocr": False,
"whole_machine_scan": False,
"external_high_relevance_scan": False,
"semantic_promotion": False,
"boundary": "Fast manifest only: hashes and cheap text stats now; heavy OCR and whole-machine scan are queued.",
}
elif scope == "external":
files = _iter_external_files()
path_label = _external_path
classify = _classify_external_path
root_bucket = _external_root_bucket
scan_roots = [str(path) for path in EXTERNAL_SCAN_ROOTS]
json_report = EXTERNAL_REPORT_JSON
markdown_report = EXTERNAL_REPORT_MD
title = "External High-Relevance Inventory Manifest"
mode = {
"heavy_ocr": False,
"whole_machine_scan": False,
"external_high_relevance_scan": True,
"semantic_promotion": False,
"boundary": "External scan is high-relevance only. It records hashes and extraction states without copying private source text into repo truth.",
}
elif scope == "extraction-queue":
return build_extraction_queue(write=write)
else:
raise ValueError(f"Unsupported scope: {scope}")
by_path, summary, root_summary = _build_index(
files=files,
path_label=path_label,
classify=classify,
root_bucket=root_bucket,
)
status = (
"pass"
if summary["unhashed_files"] == 0
and summary["unclassified_files"] == 0
and summary["unknown_extraction_status"] == 0
else "fail"
)
report = {
"scope": scope,
"status": status,
"generated_at": datetime.now(timezone.utc).isoformat(),
"mode": {
"heavy_ocr": False,
"whole_machine_scan": False,
"semantic_promotion": False,
"boundary": "Fast manifest only: hashes and cheap text stats now; heavy OCR and whole-machine scan are queued.",
},
"scan_roots": PROJECT_SCAN_ROOTS,
"mode": mode,
"scan_roots": scan_roots,
"summary": summary,
"root_summary": dict(sorted(root_summary.items())),
"root_summary": root_summary,
"by_path": by_path,
"artifacts": {
"json_report": _relative(REPORT_JSON),
"markdown_report": _relative(REPORT_MD),
"json_report": _relative(json_report),
"markdown_report": _relative(markdown_report),
},
"title": title,
}
if write:
_write_reports(report)
_write_reports(report, json_report=json_report, markdown_report=markdown_report)
return report
def _write_reports(report: dict[str, Any]) -> None:
REPORT_JSON.parent.mkdir(parents=True, exist_ok=True)
REPORT_JSON.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
REPORT_MD.write_text(_render_markdown(report), encoding="utf-8")
def build_extraction_queue(*, write: bool = True) -> dict[str, Any]:
project = build_manifest(scope="project", write=False)
external = build_manifest(scope="external", write=False)
queue: list[dict[str, Any]] = []
for source_scope, manifest in [("project", project), ("external", external)]:
for item in manifest["by_path"].values():
if item["extraction_status"] not in QUEUED_EXTRACTION_STATUSES:
continue
queue.append(
{
"source_scope": source_scope,
"path": item["path"],
"suffix": item["suffix"],
"byte_count": item["byte_count"],
"sha256": item["sha256"],
"extraction_status": item["extraction_status"],
"classification": item["classification"],
"priority": item["priority"],
"promotion_status": item["promotion_status"],
}
)
queue.sort(key=lambda item: (item["extraction_status"], item["source_scope"], item["path"]))
queue_counts: dict[str, int] = {}
source_counts: dict[str, int] = {}
for item in queue:
queue_counts[item["extraction_status"]] = queue_counts.get(item["extraction_status"], 0) + 1
source_counts[item["source_scope"]] = source_counts.get(item["source_scope"], 0) + 1
report = {
"scope": "extraction-queue",
"status": "pass",
"generated_at": datetime.now(timezone.utc).isoformat(),
"mode": {
"heavy_ocr": False,
"whole_machine_scan": False,
"external_high_relevance_scan": True,
"semantic_promotion": False,
"boundary": "Queue only. It identifies PDF/image/document extraction work without performing OCR or promoting extracted text.",
},
"summary": {
"queued_files": len(queue),
"unhashed_files": sum(1 for item in queue if not item.get("sha256")),
"source_counts": dict(sorted(source_counts.items())),
},
"queue_counts": dict(sorted(queue_counts.items())),
"queue": queue,
"artifacts": {
"json_report": _relative(EXTRACTION_QUEUE_JSON),
"markdown_report": _relative(EXTRACTION_QUEUE_MD),
},
"title": "Extraction Queue Manifest",
}
if write:
_write_reports(report, json_report=EXTRACTION_QUEUE_JSON, markdown_report=EXTRACTION_QUEUE_MD)
return report
def _write_reports(report: dict[str, Any], *, json_report: Path, markdown_report: Path) -> None:
json_report.parent.mkdir(parents=True, exist_ok=True)
json_report.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
markdown_report.write_text(_render_markdown(report), encoding="utf-8")
def _render_markdown(report: dict[str, Any]) -> str:
summary = report["summary"]
if report["scope"] == "extraction-queue":
return _render_extraction_queue_markdown(report)
lines = [
"# Character-Level Inventory Manifest",
f"# {report.get('title', 'Character-Level Inventory Manifest')}",
"",
f"- status: {report['status']}",
f"- scope: {report['scope']}",
@@ -337,6 +567,40 @@ def _render_markdown(report: dict[str, Any]) -> str:
return "\n".join(lines)
def _render_extraction_queue_markdown(report: dict[str, Any]) -> str:
lines = [
f"# {report.get('title', 'Extraction Queue Manifest')}",
"",
f"- status: {report['status']}",
f"- scope: {report['scope']}",
f"- generated_at: {report['generated_at']}",
f"- queued_files: {report['summary']['queued_files']}",
f"- unhashed_files: {report['summary']['unhashed_files']}",
f"- Heavy OCR: {'enabled' if report['mode']['heavy_ocr'] else 'disabled'}",
f"- Whole-machine scan: {'enabled' if report['mode']['whole_machine_scan'] else 'disabled'}",
"",
"## Queue Counts",
"",
]
for name, count in report["queue_counts"].items():
lines.append(f"- `{name}`: {count}")
lines.extend(["", "## Source Counts", ""])
for name, count in report["summary"]["source_counts"].items():
lines.append(f"- `{name}`: {count}")
lines.extend(
[
"",
"## Boundary",
"",
report["mode"]["boundary"],
"",
"Queued files are indexed by path, size, hash, and extraction status only. Private source text is not copied into this report.",
"",
]
)
return "\n".join(lines)
def _summary_view(report: dict[str, Any]) -> dict[str, Any]:
return {
key: value
@@ -347,7 +611,7 @@ def _summary_view(report: dict[str, Any]) -> dict[str, Any]:
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--scope", default="project", choices=["project"])
parser.add_argument("--scope", default="project", choices=["project", "external", "extraction-queue"])
parser.add_argument("--no-write", action="store_true", help="Print the manifest without writing report artifacts.")
parser.add_argument("--summary-only", action="store_true", help="Print only summary fields; still writes full artifacts unless --no-write is set.")
args = parser.parse_args(argv)
@@ -99,3 +99,80 @@ def test_quality_gate_runs_character_level_manifest_without_writing_reports() ->
'[PYTHON, "scripts/character_level_inventory_manifest.py", "--scope", "project", "--no-write", "--summary-only"]'
in quality_gate
)
def test_external_manifest_indexes_high_relevance_machine_fragments_without_copying_text() -> None:
report = _run_manifest("--scope", "external", "--no-write")
assert report["scope"] == "external"
assert report["status"] == "pass"
assert report["mode"]["whole_machine_scan"] is False
assert report["mode"]["external_high_relevance_scan"] is True
assert report["summary"]["total_files"] >= 10
assert report["summary"]["unhashed_files"] == 0
assert report["summary"]["unclassified_files"] == 0
assert report["summary"]["unknown_extraction_status"] == 0
paths = set(report["by_path"])
assert any(path.endswith("/Downloads/印度占星.pdf") for path in paths)
assert any("/文件仓库/印度占星文章/" in path for path in paths)
assert any("/WorkBuddy/" in path for path in paths)
assert any("/.workbuddy/skills/jyotish-vedic-astrology/" in path for path in paths)
extraction_counts = report["summary"]["extraction_status_counts"]
assert extraction_counts["pdf_text_extraction_queued"] >= 1
assert extraction_counts["document_text_extraction_queued"] >= 1
for path, item in report["by_path"].items():
assert path.startswith("/Users/wuyongnaren/")
assert item["sha256"]
assert item["classification"] in {
"external_book_or_document",
"external_engine_fragment",
"external_historical_report",
"external_skill_fragment",
"external_archive_or_binary",
}
assert "text_preview" not in item
def test_external_manifest_writes_separate_reports() -> None:
report = _run_manifest("--scope", "external", "--summary-only")
assert "by_path" not in report
assert report["artifacts"]["json_report"] == "docs/research/character_level_external_manifest_latest.json"
assert report["artifacts"]["markdown_report"] == "docs/research/character_level_external_manifest_latest.md"
json_path = ROOT / report["artifacts"]["json_report"]
md_path = ROOT / report["artifacts"]["markdown_report"]
assert json_path.exists()
assert md_path.exists()
assert "External High-Relevance Inventory Manifest" in md_path.read_text(encoding="utf-8")
def test_extraction_queue_report_combines_project_and_external_binary_work() -> None:
report = _run_manifest("--scope", "extraction-queue")
assert report["scope"] == "extraction-queue"
assert report["status"] == "pass"
assert report["summary"]["queued_files"] >= 60
assert report["summary"]["unhashed_files"] == 0
assert report["queue_counts"]["pdf_text_extraction_queued"] >= 2
assert report["queue_counts"]["document_text_extraction_queued"] >= 10
assert report["queue_counts"]["image_ocr_queued"] >= 50
for item in report["queue"]:
assert item["sha256"]
assert item["source_scope"] in {"project", "external"}
assert item["extraction_status"] in {
"pdf_text_extraction_queued",
"image_ocr_queued",
"document_text_extraction_queued",
}
assert "text_preview" not in item
json_path = ROOT / report["artifacts"]["json_report"]
md_path = ROOT / report["artifacts"]["markdown_report"]
assert json_path.exists()
assert md_path.exists()
assert "Extraction Queue Manifest" in md_path.read_text(encoding="utf-8")