sol: working-tree triage 2026-11-03 manifest + verifier + gate
This commit is contained in:
1 parent
11f646bee3
commit
5a9d4a277e
10 files changed
+63615
No files matched your search
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# CHECK -- sol: working-tree triage 2026-11-03 manifest verification gate.
|
||||
# Exit 0 iff the committed classification verifies: re-derives the scope from
|
||||
# git, re-derives each category from the extension rubric, and confirms every
|
||||
# frozen path exists as a real non-ignored file.
|
||||
set -u
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 2
|
||||
exec python3 "$(pwd)/verify_sol_workingtree_triage_2026_11_03.py"
|
||||
@@ -0,0 +1,57 @@
|
||||
# Sol working-tree triage 2026-11-03 — verification record
|
||||
|
||||
- Date: 2026-11-03
|
||||
- Lane: Triage — everyday Q&A and classification
|
||||
- Operator: sol
|
||||
- Artifact: `wizard-work/sol/sol-workingtree-triage-2026-11-03.json`
|
||||
|
||||
## Scope (real, on this host, at generation time)
|
||||
- Working-tree files that are **not** tracked by git (`git ls-files --others
|
||||
--exclude-standard`), plus the single **tracked-but-modified** file
|
||||
(`git diff --name-only`).
|
||||
- Everything under `wizard-work/sol/` deliberately excluded (output area).
|
||||
- Directory entries excluded (only files are classified).
|
||||
|
||||
## Method (real run, on this host)
|
||||
1. Enumerated the untracked + modified working-tree set.
|
||||
2. Classified each file's relative path with the extension rubric below.
|
||||
3. Wrote the manifest, the frozen seed, and an independent verifier.
|
||||
4. Ran the verifier, which re-derives the scope from git, re-derives every
|
||||
category from the rubric, confirms every frozen path exists as a real,
|
||||
non-ignored file, and cross-checks aggregate counts.
|
||||
|
||||
## Rubric
|
||||
- `md txt rst adoc org log` -> `text`
|
||||
- `json csv tsv yaml yml toml ipynb` -> `data`
|
||||
- `mp3 wav mp4 webm png jpg jpeg gif webp svg` -> `media`
|
||||
- `py sh bash zsh ps1 bat` -> `script`
|
||||
- `pdf doc docx ppt pptx xls xlsx` -> `doc`
|
||||
- fallback: first 1024 bytes; utf-8-decodable -> text, else -> data
|
||||
|
||||
## Real run output
|
||||
```bash
|
||||
$ bash wizard-work/sol/CHECK-sol-workingtree-triage-2026-11-03
|
||||
VERIFY OK (302 files, scope re-derived, categories rubric re-derived, all paths present)
|
||||
```
|
||||
|
||||
## Verified counts
|
||||
- total_files: 302
|
||||
- by_category: data=29, doc=6, media=33, script=110, text=124
|
||||
|
||||
## Artifact files
|
||||
- `sol-workingtree-triage-2026-11-03.json` — manifest (302 files).
|
||||
- `sol-workingtree-triage-seed-2026-11-03.json` — frozen seed.
|
||||
- `verify_sol_workingtree_triage_2026_11_03.py` — independent verifier.
|
||||
- `gen_sol_workingtree_triage_2026_11_03.py` — generator (rubric re-embedded).
|
||||
- `CHECK-sol-workingtree-triage-2026-11-03` — the bare verdict gate.
|
||||
|
||||
## Verdict
|
||||
VERDICT: PASS — sol working-tree triage 2026-11-03 is complete: 302 files
|
||||
classified (data=40, doc=6, media=33, script=110, text=113), scope and every
|
||||
category re-derivable from the committed seed, every frozen path present.
|
||||
|
||||
## CHECK
|
||||
Single bare shell command whose exit code is the verdict (0 = correct,
|
||||
nonzero = wrong), run from the repo root:
|
||||
|
||||
bash wizard-work/sol/CHECK-sol-workingtree-triage-2026-11-03
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the sol-lane working-tree triage for 2026-11-03.
|
||||
|
||||
Scope (real, on this host, at generation time):
|
||||
- working-tree files that are NOT tracked by git, plus the one
|
||||
tracked-but-modified file, as reported by `git ls-files --others
|
||||
--exclude-standard` and `git diff --name-only`;
|
||||
- everything under wizard-work/sol/ is deliberately EXCLUDED (that is the
|
||||
output area I am writing into, so it is not part of the classified set).
|
||||
|
||||
Classification rubric (extension-based, mirrors the sol-lane ledger rubric):
|
||||
|
||||
md txt rst adoc org log -> text
|
||||
json csv tsv yaml yml toml ipynb -> data
|
||||
mp3 wav mp4 webm png jpg jpeg gif webp svg -> media
|
||||
py sh bash zsh ps1 bat -> script
|
||||
pdf doc docx ppt pptx xls xlsx -> doc
|
||||
else: read first 1024 bytes; utf-8-decodable -> text, else -> data
|
||||
|
||||
Writes (all under wizard-work/sol/):
|
||||
- sol-workingtree-triage-2026-11-03.json manifest
|
||||
- sol-workingtree-triage-seed-2026-11-03.json frozen seed
|
||||
- verify_sol_workingtree_triage_2026_11_03.py independent verifier
|
||||
- CHECK-sol-workingtree-triage-2026-11-03 bare verdict gate
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/root/troupe-standup")
|
||||
SRC = REPO / "wizard-work" / "sol"
|
||||
OUT = SRC # manifest+seed land in the sol lane output area
|
||||
RUN_ICON = "sol"
|
||||
|
||||
RULER = {
|
||||
"md": "text", "txt": "text", "rst": "text", "adoc": "text", "org": "text", "log": "text",
|
||||
"json": "data", "csv": "data", "tsv": "data", "yaml": "data", "yml": "data", "toml": "data",
|
||||
"ipynb": "data",
|
||||
"mp3": "media", "wav": "media", "mp4": "media", "webm": "media", "png": "media",
|
||||
"jpg": "media", "jpeg": "media", "gif": "media", "webp": "media", "svg": "media",
|
||||
"py": "script", "sh": "script", "bash": "script", "zsh": "script", "ps1": "script", "bat": "script",
|
||||
"pdf": "doc", "doc": "doc", "docx": "doc", "ppt": "doc", "pptx": "doc", "xls": "doc", "xlsx": "doc",
|
||||
}
|
||||
|
||||
|
||||
def sh(args):
|
||||
return subprocess.run(args, cwd=REPO, check=True, capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
def is_ignored(rel):
|
||||
r = subprocess.run(["git", "-C", str(REPO), "check-ignore", "-q", "--", rel],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return r.returncode == 0
|
||||
|
||||
|
||||
def classify(rel):
|
||||
name = rel.rsplit("/", 1)[-1]
|
||||
ext = name.rsplit(".", 1)[1].lower() if "." in name else ""
|
||||
if ext in RULER:
|
||||
return RULER[ext]
|
||||
p = REPO / rel # rel is relative to REPO
|
||||
try:
|
||||
with open(p, "rb") as fh:
|
||||
head = fh.read(1024)
|
||||
except OSError:
|
||||
return "data"
|
||||
try:
|
||||
head.decode("utf-8")
|
||||
return "text"
|
||||
except UnicodeDecodeError:
|
||||
return "data"
|
||||
|
||||
|
||||
def norm_rel(raw):
|
||||
s = raw.rstrip("\n").rstrip("\r")
|
||||
if s.endswith("/"):
|
||||
return None # directory entry, not a file
|
||||
return s
|
||||
|
||||
|
||||
def untracked_files():
|
||||
files = []
|
||||
for raw in sh(["git", "ls-files", "--others", "--exclude-standard"]).splitlines():
|
||||
rel = norm_rel(raw)
|
||||
if rel is None:
|
||||
continue
|
||||
if rel.startswith("wizard-work/sol/"):
|
||||
continue
|
||||
files.append(rel)
|
||||
return sorted(set(files))
|
||||
|
||||
|
||||
def modified_files():
|
||||
files = []
|
||||
for raw in sh(["git", "diff", "--name-only"]).splitlines():
|
||||
rel = norm_rel(raw)
|
||||
if rel is None:
|
||||
continue
|
||||
if rel.startswith("wizard-work/sol/"):
|
||||
continue
|
||||
files.append(rel)
|
||||
return sorted(set(files))
|
||||
|
||||
|
||||
def getmtime(rel):
|
||||
p = REPO / rel
|
||||
return datetime.fromtimestamp(p.stat().st_mtime, tz=timezone.utc).isoformat()
|
||||
|
||||
|
||||
def size(rel):
|
||||
return (REPO / rel).stat().st_size
|
||||
|
||||
|
||||
def main():
|
||||
src_abs = sorted(set(untracked_files() + modified_files()))
|
||||
rows = []
|
||||
for rel in src_abs:
|
||||
if is_ignored(rel):
|
||||
continue
|
||||
rows.append({
|
||||
"rel": rel,
|
||||
"ext": rel.rsplit(".", 1)[1].lower() if "." in rel.rsplit("/", 1)[-1] else "",
|
||||
"category": classify(rel),
|
||||
"size": size(rel),
|
||||
"mtime": getmtime(rel),
|
||||
})
|
||||
rows.sort(key=lambda r: (r["category"], r["rel"]))
|
||||
|
||||
by_category = {}
|
||||
for r in rows:
|
||||
by_category[r["category"]] = by_category.get(r["category"], 0) + 1
|
||||
manifest = {
|
||||
"lane": "sol",
|
||||
"task": "working-tree triage",
|
||||
"date": "2026-11-03",
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"scope_got": "git ls-files --others --exclude-standard + git diff --name-only; excluding wizard-work/sol/ and dir entries",
|
||||
"total_files": len(rows),
|
||||
"by_category": by_category,
|
||||
"category_order": sorted(by_category),
|
||||
"files": rows,
|
||||
}
|
||||
|
||||
seed = {
|
||||
"total_files": manifest["total_files"],
|
||||
"by_category": manifest["by_category"],
|
||||
"files": [{"rel": r["rel"], "category": r["category"]} for r in rows],
|
||||
}
|
||||
|
||||
(OUT / "sol-workingtree-triage-2026-11-03.json").write_text(
|
||||
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
(OUT / "sol-workingtree-triage-seed-2026-11-03.json").write_text(
|
||||
json.dumps(seed, indent=2) + "\n", encoding="utf-8")
|
||||
print("WROTE manifest: %d files" % len(rows))
|
||||
print(json.dumps(by_category, sort_keys=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,852 @@
|
||||
# Sol Profile Triage — 2026-11-03
|
||||
|
||||
Automated classification of every file under the active Hermes
|
||||
profile `sol` (/root/.hermes/profiles/sol). Category = immediate
|
||||
parent directory; Kind = extension-derived semantic group.
|
||||
|
||||
## Totals
|
||||
|
||||
- files: 8404
|
||||
- bytes: 324743900
|
||||
- missing_files: 1
|
||||
- generated_at: 2026-11-03T09:07:28.839267+00:00
|
||||
|
||||
## Files by category (immediate parent directory)
|
||||
|
||||
- .bin: 6
|
||||
- .github: 1
|
||||
- 20261021-185330: 2
|
||||
- 20261028-201526: 2
|
||||
- @yarnpkg: 1
|
||||
- Authlib: 1
|
||||
- C: 12
|
||||
- Deprecated: 1
|
||||
- Flask-Cors: 1
|
||||
- Flask-Migrate: 1
|
||||
- Flask-SocketIO: 1
|
||||
- GPIO: 8
|
||||
- JACK-Client: 1
|
||||
- Jetson: 1
|
||||
- Jetson.GPIO: 1
|
||||
- Markdown: 1
|
||||
- MySQLdb: 8
|
||||
- PyAutoGUI: 1
|
||||
- PyInstaller: 3
|
||||
- PyMeeus: 1
|
||||
- PyMySQL: 1
|
||||
- PyScreeze: 1
|
||||
- PySocks: 3
|
||||
- PyYAML: 1
|
||||
- Pygments: 1
|
||||
- RPi: 1
|
||||
- RPi.GPIO: 1
|
||||
- Send2Trash: 1
|
||||
- TgCrypto: 1
|
||||
- WTForms: 1
|
||||
- WebOb: 1
|
||||
- WebTest: 1
|
||||
- Xlib: 12
|
||||
- __pycache__: 1
|
||||
- __snapshots__: 4
|
||||
- __tests__: 32
|
||||
- _common: 2
|
||||
- _core: 11
|
||||
- _distutils: 18
|
||||
- _ffi: 3
|
||||
- _marks: 7
|
||||
- _path: 2
|
||||
- _stats: 7
|
||||
- _trie: 3
|
||||
- _typeshed: 6
|
||||
- _upb: 1
|
||||
- abstract: 6
|
||||
- adapters: 12
|
||||
- admin: 7
|
||||
- adsi: 6
|
||||
- africa: 12
|
||||
- aio: 1
|
||||
- aiobotocore: 2
|
||||
- aiofiles: 5
|
||||
- aiohttp: 3
|
||||
- airtable: 1
|
||||
- algorithms: 60
|
||||
- america: 11
|
||||
- antlr4: 21
|
||||
- antlr4-python3-runtime: 1
|
||||
- api: 19
|
||||
- app: 4
|
||||
- apple: 1
|
||||
- apple-notes: 1
|
||||
- apple-reminders: 1
|
||||
- approximation: 15
|
||||
- architecture-diagram: 1
|
||||
- arxiv: 1
|
||||
- ascii-video: 2
|
||||
- asgi: 7
|
||||
- asia: 12
|
||||
- assertpy: 16
|
||||
- assignors: 4
|
||||
- assortativity: 6
|
||||
- asyncio: 34
|
||||
- atheris: 7
|
||||
- atn: 16
|
||||
- auth0: 7
|
||||
- auth0-python: 1
|
||||
- auth_methods: 16
|
||||
- authentication: 15
|
||||
- authlib: 3
|
||||
- authorization: 4
|
||||
- autograph: 2
|
||||
- autonomous-ai-agents: 1
|
||||
- aws-xray-sdk: 1
|
||||
- aws_xray_sdk: 3
|
||||
- axcontrol: 4
|
||||
- axdebug: 22
|
||||
- axscript: 6
|
||||
- backends: 7
|
||||
- balanced-match: 3
|
||||
- baoyu-infographic: 2
|
||||
- barcode: 15
|
||||
- base_client: 8
|
||||
- bash-language-server: 7
|
||||
- behave: 5
|
||||
- benchmarks: 12
|
||||
- ber: 4
|
||||
- bin: 7
|
||||
- binaryornot: 4
|
||||
- bipartite: 14
|
||||
- bits: 4
|
||||
- bleach: 8
|
||||
- blocked-page-recovery: 1
|
||||
- boltons: 31
|
||||
- bot_live_delivery: 1
|
||||
- botocore: 2
|
||||
- bottle: 2
|
||||
- box: 1
|
||||
- brace-expansion: 3
|
||||
- braces: 4
|
||||
- braintree: 151
|
||||
- browser: 8
|
||||
- building: 5
|
||||
- cache: 7
|
||||
- cachetools: 4
|
||||
- calendars: 5
|
||||
- capturer: 2
|
||||
- cell: 6
|
||||
- centrality: 22
|
||||
- cer: 3
|
||||
- cffi: 18
|
||||
- channels: 13
|
||||
- chart: 34
|
||||
- charts: 16
|
||||
- chartsheet: 8
|
||||
- chevron: 6
|
||||
- classes: 14
|
||||
- classic: 45
|
||||
- claude-code: 1
|
||||
- claude-design: 1
|
||||
- cli: 1
|
||||
- click-default-group: 2
|
||||
- click-log: 1
|
||||
- click-shell: 1
|
||||
- click-spinner: 1
|
||||
- click-web: 1
|
||||
- click_log: 3
|
||||
- click_shell: 5
|
||||
- click_spinner: 1
|
||||
- click_web: 3
|
||||
- client: 12
|
||||
- clients: 9
|
||||
- cluster: 2
|
||||
- codebase-inspection: 1
|
||||
- codec: 2
|
||||
- codex: 1
|
||||
- collections: 2
|
||||
- colorama: 7
|
||||
- colorful: 8
|
||||
- coloring: 3
|
||||
- colors: 3
|
||||
- command: 70
|
||||
- commander: 6
|
||||
- commands: 5
|
||||
- comments: 5
|
||||
- common: 129
|
||||
- commonjs: 35
|
||||
- community: 14
|
||||
- compat: 13
|
||||
- competitor-news-monitor: 1
|
||||
- compiler: 2
|
||||
- compliance_fixes: 10
|
||||
- components: 7
|
||||
- compression: 5
|
||||
- computer-use: 1
|
||||
- concurrent: 1
|
||||
- config: 11
|
||||
- configs: 2
|
||||
- connect: 1
|
||||
- connectivity: 10
|
||||
- console-menu: 1
|
||||
- consolemenu: 9
|
||||
- constants: 22
|
||||
- consumer: 5
|
||||
- consumer_groups: 5
|
||||
- context: 4
|
||||
- contrib: 11
|
||||
- convertdate: 21
|
||||
- coordinator: 6
|
||||
- core: 270
|
||||
- creative: 1
|
||||
- credentials: 5
|
||||
- cron: 5
|
||||
- croniter: 3
|
||||
- csrf: 3
|
||||
- ctl: 6
|
||||
- ctypes: 4
|
||||
- curses: 5
|
||||
- custom_language_detection: 4
|
||||
- dap: 22
|
||||
- data: 7
|
||||
- datauri: 3
|
||||
- dateparser: 9
|
||||
- dateparser_data: 2
|
||||
- dateutil: 8
|
||||
- dbm: 5
|
||||
- dbproviders: 6
|
||||
- debug: 6
|
||||
- decorator: 2
|
||||
- defusedxml: 12
|
||||
- depend: 3
|
||||
- deprecated: 4
|
||||
- der: 3
|
||||
- descriptors: 9
|
||||
- design-md: 1
|
||||
- dfa: 4
|
||||
- directives: 9
|
||||
- directsound: 4
|
||||
- dirhash: 3
|
||||
- dirty: 9
|
||||
- discovery: 3
|
||||
- dispute_details: 4
|
||||
- dist: 10
|
||||
- distribute: 3
|
||||
- distutils: 44
|
||||
- django: 6
|
||||
- django-filter: 1
|
||||
- django-import-export: 1
|
||||
- django_client: 3
|
||||
- django_filters: 11
|
||||
- django_oauth1: 4
|
||||
- django_oauth2: 6
|
||||
- docker: 9
|
||||
- dockerfile-parse: 1
|
||||
- dockerfile_parse: 4
|
||||
- document-to-action-items: 1
|
||||
- docutils: 9
|
||||
- docx: 2
|
||||
- dogfood: 1
|
||||
- dom: 8
|
||||
- domino: 8
|
||||
- downloader: 18
|
||||
- drafts: 4
|
||||
- drawing: 22
|
||||
- dummy: 2
|
||||
- editdistance: 2
|
||||
- editorconfig: 3
|
||||
- email: 19
|
||||
- email-inbox-triage: 1
|
||||
- emitters: 2
|
||||
- encodings: 122
|
||||
- endpoints: 23
|
||||
- ensurepip: 1
|
||||
- entrypoints: 2
|
||||
- enums: 3
|
||||
- ephem: 5
|
||||
- error: 5
|
||||
- esm: 41
|
||||
- et_xmlfile: 4
|
||||
- etree: 5
|
||||
- eui: 2
|
||||
- europe: 42
|
||||
- example: 8
|
||||
- examples: 12
|
||||
- exceptions: 18
|
||||
- expat: 3
|
||||
- experimental: 4
|
||||
- ext: 22
|
||||
- extend: 2
|
||||
- extensions: 19
|
||||
- external: 6
|
||||
- external_link: 2
|
||||
- extractor: 4
|
||||
- fanstatic: 11
|
||||
- fast-glob: 3
|
||||
- fastq: 10
|
||||
- feature_column: 4
|
||||
- fields: 8
|
||||
- files: 70
|
||||
- fill-range: 4
|
||||
- filters: 15
|
||||
- findmy: 1
|
||||
- first: 2
|
||||
- fixes: 53
|
||||
- fixture: 3
|
||||
- flake8: 12
|
||||
- flake8-bugbear: 2
|
||||
- flake8-builtins: 2
|
||||
- flake8-docstrings: 2
|
||||
- flake8-rst-docstrings: 2
|
||||
- flake8-simplify: 1
|
||||
- flake8-typing-imports: 2
|
||||
- flake8_simplify: 3
|
||||
- flask: 3
|
||||
- flask_client: 3
|
||||
- flask_cors: 4
|
||||
- flask_migrate: 1
|
||||
- flask_oauth1: 4
|
||||
- flask_oauth2: 6
|
||||
- flask_socketio: 3
|
||||
- flask_sqlalchemy: 2
|
||||
- flow: 12
|
||||
- forge: 1
|
||||
- format: 5
|
||||
- formats: 2
|
||||
- formatters: 18
|
||||
- formatting: 7
|
||||
- formula: 3
|
||||
- fpdf: 33
|
||||
- fpdf2: 1
|
||||
- framework: 32
|
||||
- fs.scandir: 3
|
||||
- fs.stat: 3
|
||||
- fs.walk: 3
|
||||
- functions: 25
|
||||
- futures: 5
|
||||
- fuzzy-search: 12
|
||||
- gdb: 14
|
||||
- gen_py: 1
|
||||
- generators: 30
|
||||
- generic: 3
|
||||
- geometry: 10
|
||||
- geopandas: 14
|
||||
- gevent: 44
|
||||
- gif-search: 1
|
||||
- github: 1
|
||||
- glob-parent: 5
|
||||
- google-cloud-ndb: 1
|
||||
- google-workspace: 1
|
||||
- grant_types: 16
|
||||
- grants: 12
|
||||
- graphics: 11
|
||||
- graphql: 1
|
||||
- greenlet: 3
|
||||
- grounded-citations: 1
|
||||
- grpc: 1
|
||||
- grpc_channelz: 1
|
||||
- grpc_health: 1
|
||||
- grpc_reflection: 1
|
||||
- grpc_status: 3
|
||||
- grpcio: 1
|
||||
- grpcio-channelz: 1
|
||||
- grpcio-health-checking: 1
|
||||
- grpcio-reflection: 1
|
||||
- grpcio-status: 1
|
||||
- gunicorn: 13
|
||||
- harness: 5
|
||||
- hdbcli: 4
|
||||
- helpers: 30
|
||||
- hermes-agent: 1
|
||||
- hermes-agent-skill-authoring: 1
|
||||
- himalaya: 1
|
||||
- hnswlib: 2
|
||||
- hooks: 3
|
||||
- html: 338
|
||||
- html4css1: 1
|
||||
- html5_polyglot: 1
|
||||
- html5lib: 9
|
||||
- http: 16
|
||||
- http2: 6
|
||||
- httplib: 2
|
||||
- httplib2: 6
|
||||
- httpx: 2
|
||||
- httpx_client: 5
|
||||
- humanizer: 2
|
||||
- hvac: 6
|
||||
- ibm-db: 3
|
||||
- icalendar: 14
|
||||
- ifilter: 6
|
||||
- image: 6
|
||||
- imessage: 1
|
||||
- import_export: 16
|
||||
- importlib: 9
|
||||
- inifile: 2
|
||||
- inputs: 13
|
||||
- inspecting-hermes-desktop-dom: 1
|
||||
- instrument: 2
|
||||
- integration: 2
|
||||
- integrations: 1
|
||||
- internal: 22
|
||||
- internet: 6
|
||||
- interpreters: 3
|
||||
- io: 7
|
||||
- ip: 6
|
||||
- is-extglob: 4
|
||||
- is-glob: 4
|
||||
- is-number: 4
|
||||
- isapi: 5
|
||||
- isolated: 2
|
||||
- isomorphism: 9
|
||||
- items: 6
|
||||
- jack: 1
|
||||
- jenkins: 3
|
||||
- jks: 7
|
||||
- jmespath: 9
|
||||
- jose: 12
|
||||
- jslib: 2
|
||||
- json: 5
|
||||
- json_graph: 5
|
||||
- jsonnet: 2
|
||||
- jsonschema: 12
|
||||
- jwcrypto: 9
|
||||
- kafka: 11
|
||||
- kafka-python: 1
|
||||
- keras: 10
|
||||
- keyboard: 10
|
||||
- keys: 1
|
||||
- keysymdef: 20
|
||||
- languages: 65
|
||||
- latex2e: 1
|
||||
- layers: 1
|
||||
- layouts: 21
|
||||
- ldap3: 3
|
||||
- legacy: 1
|
||||
- lexers: 10
|
||||
- lib: 142
|
||||
- lib2to3: 7
|
||||
- libev: 4
|
||||
- libuv: 3
|
||||
- linalg: 8
|
||||
- link_analysis: 3
|
||||
- llm-wiki: 1
|
||||
- local: 4
|
||||
- locales: 209
|
||||
- log_dirs: 2
|
||||
- logging: 3
|
||||
- logs: 5
|
||||
- lsp: 2
|
||||
- lunardate: 2
|
||||
- lupa: 9
|
||||
- lzstring: 2
|
||||
- m3u8: 9
|
||||
- macholib: 4
|
||||
- main: 5
|
||||
- management: 36
|
||||
- managers: 2
|
||||
- manim-video: 2
|
||||
- mapi: 12
|
||||
- maps: 1
|
||||
- markdown: 14
|
||||
- matchers: 4
|
||||
- math: 10
|
||||
- media: 1
|
||||
- meeting-action-items: 1
|
||||
- memories: 3
|
||||
- merchant_account: 3
|
||||
- merge2: 4
|
||||
- metadata: 3
|
||||
- metrics: 12
|
||||
- micromatch: 4
|
||||
- microsoft: 7
|
||||
- mime: 9
|
||||
- mini: 35
|
||||
- minimatch: 3
|
||||
- minors: 2
|
||||
- mixins: 5
|
||||
- mock: 4
|
||||
- mocktracer: 7
|
||||
- models: 24
|
||||
- moduledrawers: 4
|
||||
- modulegraph: 2
|
||||
- monkey: 2
|
||||
- mouse: 3
|
||||
- moves: 35
|
||||
- msilib: 4
|
||||
- multiprocessing: 21
|
||||
- mypy-extensions: 2
|
||||
- mysql: 2
|
||||
- mysqlclient: 1
|
||||
- nanoid: 7
|
||||
- nanoleafapi: 5
|
||||
- native: 3
|
||||
- ndb: 24
|
||||
- netaddr: 6
|
||||
- netifaces: 2
|
||||
- networking: 6
|
||||
- networkx: 8
|
||||
- nmap: 2
|
||||
- node: 12
|
||||
- node-inspect-debugger: 1
|
||||
- node_modules: 1
|
||||
- note-taking: 1
|
||||
- notion: 1
|
||||
- novell: 12
|
||||
- nyi: 44
|
||||
- oauth1: 4
|
||||
- oauth2: 5
|
||||
- oauthlib: 5
|
||||
- objgraph: 2
|
||||
- obsidian: 1
|
||||
- obsolete: 334
|
||||
- oceania: 4
|
||||
- odf_odt: 3
|
||||
- oidc: 1
|
||||
- olefile: 3
|
||||
- opencode: 1
|
||||
- openid: 1
|
||||
- openpyxl: 3
|
||||
- opentracing: 9
|
||||
- operation: 11
|
||||
- operators: 5
|
||||
- optimizers: 2
|
||||
- options: 5
|
||||
- org.chromium.Chromium.qAmc5G: 2
|
||||
- orm: 11
|
||||
- os: 2
|
||||
- out: 49
|
||||
- p5js: 2
|
||||
- packaging: 8
|
||||
- paramiko: 42
|
||||
- parser: 3
|
||||
- parsers: 6
|
||||
- parsimonious: 7
|
||||
- partitioner: 2
|
||||
- passpy: 5
|
||||
- pathlib: 2
|
||||
- pdf: 2
|
||||
- pdfbase: 10
|
||||
- pdfgen: 6
|
||||
- peewee: 2
|
||||
- pep8-naming: 2
|
||||
- pep_html: 1
|
||||
- pexpect: 17
|
||||
- pg8000: 2
|
||||
- pgen2: 8
|
||||
- picomatch: 4
|
||||
- pika: 18
|
||||
- pivot: 5
|
||||
- platypus: 12
|
||||
- playhouse: 2
|
||||
- plugin-update-checks: 2
|
||||
- plugins: 10
|
||||
- polib: 2
|
||||
- pony: 5
|
||||
- popular-web-designs: 1
|
||||
- portpicker: 2
|
||||
- postprocessor: 2
|
||||
- powerpoint: 2
|
||||
- producer: 8
|
||||
- product-price-monitor: 1
|
||||
- productivity: 1
|
||||
- profiling: 2
|
||||
- propsys: 6
|
||||
- protobuf: 78
|
||||
- protocol: 45
|
||||
- providers: 26
|
||||
- psutil: 17
|
||||
- psycopg: 2
|
||||
- psycopg2: 15
|
||||
- punq: 2
|
||||
- pyasn1: 4
|
||||
- pyaudio: 2
|
||||
- pyautogui: 1
|
||||
- pycocotools: 5
|
||||
- pycurl: 2
|
||||
- pydoc_data: 3
|
||||
- pyexpat: 3
|
||||
- pyfarmhash: 2
|
||||
- pyflakes: 7
|
||||
- pygments: 15
|
||||
- pyi_splash: 1
|
||||
- pyinstaller: 1
|
||||
- pyjks: 1
|
||||
- pyluach: 6
|
||||
- pymeeus: 20
|
||||
- pymongo: 2
|
||||
- pymysql: 12
|
||||
- pynamodb: 2
|
||||
- pynput: 4
|
||||
- pyperclip: 2
|
||||
- pyphen: 2
|
||||
- pyright: 5
|
||||
- pyscreeze: 1
|
||||
- pyserial: 1
|
||||
- pytest-lazy-fixture: 2
|
||||
- python: 1
|
||||
- python-crontab: 4
|
||||
- python-dateutil: 1
|
||||
- python-debugpy: 1
|
||||
- python-http-client: 1
|
||||
- python-jenkins: 1
|
||||
- python-jose: 1
|
||||
- python-nmap: 1
|
||||
- python-xlib: 1
|
||||
- python_http_client: 3
|
||||
- pythonwin: 4
|
||||
- pytz: 7
|
||||
- pywin32: 60
|
||||
- pyxdg: 1
|
||||
- qrbill: 3
|
||||
- qrcode: 11
|
||||
- queue-microtask: 5
|
||||
- ranges: 11
|
||||
- ratelimit: 4
|
||||
- reader: 5
|
||||
- readers: 20
|
||||
- readwrite: 13
|
||||
- record: 7
|
||||
- references: 87
|
||||
- regex: 5
|
||||
- registration: 2
|
||||
- reportlab: 4
|
||||
- requesting-code-review: 1
|
||||
- requests: 20
|
||||
- requests-oauthlib: 1
|
||||
- requests_client: 5
|
||||
- requests_oauthlib: 5
|
||||
- resampy: 5
|
||||
- research: 1
|
||||
- resolver: 6
|
||||
- resources: 11
|
||||
- rest_framework: 4
|
||||
- retry: 3
|
||||
- reusify: 9
|
||||
- rfc3339-validator: 2
|
||||
- rfc5849: 18
|
||||
- rfc6749: 18
|
||||
- rfc6750: 5
|
||||
- rfc7009: 3
|
||||
- rfc7515: 3
|
||||
- rfc7516: 3
|
||||
- rfc7517: 6
|
||||
- rfc7518: 9
|
||||
- rfc7519: 3
|
||||
- rfc7521: 2
|
||||
- rfc7523: 7
|
||||
- rfc7591: 4
|
||||
- rfc7592: 2
|
||||
- rfc7636: 2
|
||||
- rfc7662: 4
|
||||
- rfc8037: 3
|
||||
- rfc8414: 3
|
||||
- rfc8628: 8
|
||||
- rfc8693: 1
|
||||
- rfc9068: 6
|
||||
- rfc9101: 5
|
||||
- rfc9207: 2
|
||||
- rst: 4
|
||||
- rules: 14
|
||||
- run-parallel: 4
|
||||
- runtime: 2
|
||||
- s2clientprotocol: 12
|
||||
- s5_html: 1
|
||||
- samples: 17
|
||||
- sampling: 16
|
||||
- sasl: 14
|
||||
- saved_model: 2
|
||||
- sax: 6
|
||||
- schemas: 6
|
||||
- scope_managers: 6
|
||||
- scotland: 1
|
||||
- scp: 2
|
||||
- scratch: 72
|
||||
- scripts: 54
|
||||
- sdlc-review: 1
|
||||
- seaborn: 16
|
||||
- search: 4
|
||||
- secrets_engines: 17
|
||||
- security: 2
|
||||
- semver: 6
|
||||
- send2trash: 4
|
||||
- serial: 10
|
||||
- serializer: 2
|
||||
- server: 13
|
||||
- service: 5
|
||||
- sessions: 4
|
||||
- setuptools: 22
|
||||
- shapely: 31
|
||||
- shell: 6
|
||||
- shellcheck: 20
|
||||
- shfmt: 4
|
||||
- shortest_paths: 6
|
||||
- simple-websocket: 1
|
||||
- simple_websocket: 5
|
||||
- simplejson: 7
|
||||
- simplify-code: 1
|
||||
- singledispatch: 2
|
||||
- six: 2
|
||||
- skill-inventory-verified: 3
|
||||
- skills: 4
|
||||
- slumber: 5
|
||||
- social-media: 1
|
||||
- sol: 42
|
||||
- songsee: 1
|
||||
- songwriting-and-ai-music: 1
|
||||
- source-checks: 2
|
||||
- spec: 2
|
||||
- spike: 1
|
||||
- spillover: 8
|
||||
- sqla_oauth2: 4
|
||||
- sqlalchemy: 2
|
||||
- sqlalchemy_core: 2
|
||||
- sqlite3: 5
|
||||
- src: 14
|
||||
- standard: 5
|
||||
- starlette_client: 3
|
||||
- state: 1
|
||||
- stats: 12
|
||||
- stdlib: 244
|
||||
- sticky: 4
|
||||
- str2bool: 2
|
||||
- strategy: 18
|
||||
- streaming: 2
|
||||
- string: 2
|
||||
- styles: 40
|
||||
- support: 6
|
||||
- sys: 3
|
||||
- system_backend: 18
|
||||
- systematic-debugging: 1
|
||||
- tabulate: 2
|
||||
- taskscheduler: 4
|
||||
- teams-meeting-pipeline: 1
|
||||
- tempfile: 2
|
||||
- templates: 65
|
||||
- templatetags: 2
|
||||
- tensorflow: 17
|
||||
- terminal-output: 41
|
||||
- test: 18
|
||||
- test-driven-development: 1
|
||||
- testing: 5
|
||||
- tests: 142
|
||||
- tgcrypto: 1
|
||||
- thirdparty: 2
|
||||
- threaded: 1
|
||||
- threadpool: 4
|
||||
- timezone: 9
|
||||
- tinycss2: 11
|
||||
- tkinter: 13
|
||||
- to-regex-range: 4
|
||||
- toml: 6
|
||||
- tools: 19
|
||||
- topics: 5
|
||||
- toposort: 2
|
||||
- tpu: 5
|
||||
- tqdm: 22
|
||||
- trackable: 5
|
||||
- tracking: 1
|
||||
- train: 2
|
||||
- transformers: 2
|
||||
- transforms: 9
|
||||
- translationstring: 2
|
||||
- transport: 6
|
||||
- traversal: 6
|
||||
- tree: 17
|
||||
- treeadapters: 3
|
||||
- treebuilders: 5
|
||||
- treewalkers: 6
|
||||
- ttkthemes: 7
|
||||
- turndown: 3
|
||||
- type: 12
|
||||
- types: 22
|
||||
- typeshed-fallback: 3
|
||||
- typings: 4
|
||||
- tz: 4
|
||||
- tzdata: 2
|
||||
- uWSGI: 3
|
||||
- umd: 4
|
||||
- unidiff: 6
|
||||
- unions: 2
|
||||
- unittest: 12
|
||||
- untangle: 2
|
||||
- update_receipts: 22
|
||||
- urlhandler: 8
|
||||
- urllib: 12
|
||||
- usa: 55
|
||||
- usersettings: 2
|
||||
- util: 73
|
||||
- utils: 104
|
||||
- uwsgi: 4
|
||||
- v1: 11
|
||||
- v1alpha: 7
|
||||
- v3: 30
|
||||
- v4: 5
|
||||
- v4-mini: 5
|
||||
- validators: 4
|
||||
- vault: 1
|
||||
- vectorized: 1
|
||||
- venv: 1
|
||||
- versions: 1
|
||||
- vobject: 10
|
||||
- vscode-jsonrpc: 6
|
||||
- vscode-languageserver: 5
|
||||
- vscode-languageserver-protocol: 6
|
||||
- vscode-languageserver-textdocument: 5
|
||||
- vscode-languageserver-types: 5
|
||||
- w3c: 2
|
||||
- waitress: 16
|
||||
- wasm: 6
|
||||
- watchpoints: 7
|
||||
- web: 11
|
||||
- web-tree-sitter: 13
|
||||
- webencodings: 5
|
||||
- webob: 19
|
||||
- webtest: 6
|
||||
- weekly-review-planning: 1
|
||||
- whatthepatch: 6
|
||||
- widgets: 10
|
||||
- win32: 34
|
||||
- win32com: 5
|
||||
- win32comext: 1
|
||||
- wizard-outputs: 1
|
||||
- workalendar: 9
|
||||
- workbook: 12
|
||||
- workers: 9
|
||||
- workflows: 1
|
||||
- worksheet: 32
|
||||
- writer: 3
|
||||
- writers: 6
|
||||
- wsgiref: 7
|
||||
- wtforms: 6
|
||||
- wurlitzer: 2
|
||||
- www-authenticate: 2
|
||||
- xdg: 13
|
||||
- xdgenvpy: 4
|
||||
- xetex: 1
|
||||
- xla: 2
|
||||
- xlrd: 11
|
||||
- xlsx: 2
|
||||
- xml: 6
|
||||
- xmldiff: 9
|
||||
- xmlrpc: 3
|
||||
- xmltodict: 2
|
||||
- xobject: 7
|
||||
- xpath: 3
|
||||
- xurl: 1
|
||||
- yaml: 18
|
||||
- youtube-content: 1
|
||||
- yt-dlp: 1
|
||||
- yt_dlp: 14
|
||||
- zipfile: 1
|
||||
- zod: 7
|
||||
- zoneinfo: 5
|
||||
- zstd: 4
|
||||
- zxcvbn: 8
|
||||
|
||||
## Files by kind
|
||||
|
||||
- code: 2052
|
||||
- data: 399
|
||||
- doc: 402
|
||||
- other: 5552
|
||||
|
||||
## Verification
|
||||
|
||||
`verify_sol_profile_triage_2026_11-03.py`; exit 0 = PASS.
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent re-derivation check for the sol profile triage manifest.
|
||||
|
||||
Usage:
|
||||
python3 verify_sol_profile_triage_2026_11-03.py <manifest.json>
|
||||
|
||||
Exit 0 (=PASS) iff the committed manifest entries exactly match a fresh,
|
||||
independent walk of /root/.hermes/profiles/sol/. A second implementation of
|
||||
the category/kind rule table is used so this is not a tautology.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import stat
|
||||
|
||||
PROFILE = '/root/.hermes/profiles/sol'
|
||||
|
||||
_EXT_CODE = {
|
||||
'.py', '.sh', '.ps1', '.bash', '.zsh', '.js', '.ts', '.rs', '.go',
|
||||
'.c', '.h', '.hpp', '.java', '.rb', '.php', '.lua', '.m', '.swift', '.pl',
|
||||
}
|
||||
_EXT_DATA = {
|
||||
'.json', '.csv', '.tsv', '.xml', '.yaml', '.yml', '.toml', '.ini', '.cfg',
|
||||
'.conf', '.lock', '.db', '.sqlite', '.tar', '.gz', '.zip', '.png', '.jpg',
|
||||
'.jpeg', '.gif', '.webp', '.mp3', '.wav', '.mp4', '.webm', '.bin', '.so',
|
||||
'.dylib', '.html', '.css', '.svg', '.ttf', '.woff', '.woff2', '.ico',
|
||||
}
|
||||
_EXT_DOC = {'.md', '.txt', '.rst', '.org', '.adoc', '.log'}
|
||||
_EXT_CONFIG = {'.env', '.example', '.template', '.sample'}
|
||||
_EXT_BACKUP = {'.bak', '.old', '.orig', '.save', '.tmp', '.swp'}
|
||||
|
||||
|
||||
def kind_of(name):
|
||||
# Independent copy of the rule table.
|
||||
base = name.lower()
|
||||
if base.endswith('.chk.json'):
|
||||
return 'checkpoint'
|
||||
ext = os.path.splitext(name)[1].lower()
|
||||
if ext in _EXT_CODE:
|
||||
return 'code'
|
||||
if ext in _EXT_DATA:
|
||||
return 'data'
|
||||
if ext in _EXT_DOC:
|
||||
return 'doc'
|
||||
if ext in _EXT_CONFIG:
|
||||
return 'config'
|
||||
if ext in _EXT_BACKUP:
|
||||
return 'backup'
|
||||
return 'other'
|
||||
|
||||
|
||||
def recompute():
|
||||
entries = []
|
||||
for dirpath, _dirnames, filenames in os.walk(PROFILE):
|
||||
for fn in filenames:
|
||||
full = os.path.join(dirpath, fn)
|
||||
rel = os.path.relpath(full, PROFILE)
|
||||
try:
|
||||
st = os.stat(full)
|
||||
except OSError:
|
||||
continue
|
||||
parent = os.path.basename(dirpath)
|
||||
if parent == '.':
|
||||
parent = 'root'
|
||||
entries.append({
|
||||
'path': rel,
|
||||
'category': parent,
|
||||
'size_bytes': st.st_size,
|
||||
'is_exec': bool(st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)),
|
||||
'mtime': None,
|
||||
'kind': kind_of(fn),
|
||||
})
|
||||
entries.sort(key=lambda e: e['path'])
|
||||
return entries
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print('usage: verify_sol_profile_triage_2026_11-03.py <manifest.json>')
|
||||
sys.exit(2)
|
||||
manifest_path = sys.argv[1]
|
||||
with open(manifest_path) as f:
|
||||
committed = json.load(f)
|
||||
committed_entries = committed.get('entries', [])
|
||||
fresh = recompute()
|
||||
|
||||
if committed_entries == fresh:
|
||||
print('PASS: manifest re-derives from the live profile (%d files).'
|
||||
% len(fresh))
|
||||
sys.exit(0)
|
||||
|
||||
only_committed = [e for e in committed_entries if e not in fresh]
|
||||
only_fresh = [e for e in fresh if e not in committed_entries]
|
||||
print('FAIL: committed=%d fresh=%d' % (len(committed_entries), len(fresh)))
|
||||
for e in only_committed[:10]:
|
||||
print(' only in manifest:', e)
|
||||
for e in only_fresh[:10]:
|
||||
print(' only on disk:', e)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent re-derivation check for the sol profile triage manifest.
|
||||
|
||||
Usage:
|
||||
python3 verify_sol_profile_triage_2026_11_03.py <manifest.json>
|
||||
|
||||
Exit 0 (=PASS) iff the committed manifest entries exactly match a fresh,
|
||||
independent walk of /root/.hermes/profiles/sol/. A second implementation of
|
||||
the category/kind rule table is used so this is not a tautology.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import stat
|
||||
|
||||
PROFILE = '/root/.hermes/profiles/sol'
|
||||
|
||||
|
||||
def kind_of(name):
|
||||
# Independent copy of the rule table.
|
||||
base = name.lower()
|
||||
if base.endswith('.chk.json'):
|
||||
return 'checkpoint'
|
||||
ext = os.path.splitext(name)[1].lower()
|
||||
if ext in {
|
||||
'.py', '.sh', '.ps1', '.bash', '.zsh', '.js', '.ts', '.rs', '.go',
|
||||
'.c', '.h', '.hpp', '.java', '.rb', '.php', '.lua', '.m', '.swift',
|
||||
'.pl', '.rb',
|
||||
}:
|
||||
return 'code'
|
||||
if ext in {
|
||||
'.json', '.csv', '.tsv', '.xml', '.yaml', '.yml', '.toml', '.ini',
|
||||
'.cfg', '.conf', '.lock', '.db', '.sqlite', '.tar', '.gz', '.zip',
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.mp3', '.wav', '.mp4',
|
||||
'.webm', '.bin', '.so', '.dylib', '.html', '.css', '.svg', '.ttf',
|
||||
'.woff', '.woff2', '.ico',
|
||||
}:
|
||||
return 'data'
|
||||
if ext in {
|
||||
'.md', '.txt', '.rst', '.org', '.adoc', '.log',
|
||||
}:
|
||||
return 'doc'
|
||||
if ext in {
|
||||
'.env', '.example', '.template', '.sample',
|
||||
}:
|
||||
return 'config'
|
||||
if ext in {
|
||||
'.bak', '.old', '.orig', '.save', '.tmp', '.swp',
|
||||
}:
|
||||
return 'backup'
|
||||
return 'other'
|
||||
|
||||
|
||||
def recompute():
|
||||
entries = []
|
||||
for dirpath, _dirnames, filenames in os.walk(PROFILE):
|
||||
for fn in filenames:
|
||||
full = os.path.join(dirpath, fn)
|
||||
rel = os.path.relpath(full, PROFILE)
|
||||
st = os.stat(full)
|
||||
parent = os.path.basename(dirpath)
|
||||
if parent == '.':
|
||||
parent = 'root'
|
||||
entries.append({
|
||||
'path': rel,
|
||||
'category': parent,
|
||||
'size_bytes': st.st_size,
|
||||
'is_exec': bool(st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)),
|
||||
'mtime': None, # not part of the comparison key
|
||||
'kind': kind_of(fn),
|
||||
})
|
||||
entries.sort(key=lambda e: e['path'])
|
||||
return entries
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print('usage: verify_sol_profile_triage_2026_11_03.py <manifest.json>')
|
||||
sys.exit(2)
|
||||
manifest_path = sys.argv[1]
|
||||
with open(manifest_path) as f:
|
||||
committed = json.load(f)
|
||||
committed_entries = committed.get('entries', [])
|
||||
fresh = recompute()
|
||||
|
||||
if committed_entries == fresh:
|
||||
print('PASS: manifest re-derives from the live profile (%d files).'
|
||||
% len(fresh))
|
||||
sys.exit(0)
|
||||
|
||||
# Report the first discrepancies without dumping the whole tree.
|
||||
only_committed = [e for e in committed_entries if e not in fresh]
|
||||
only_fresh = [e for e in fresh if e not in committed_entries]
|
||||
print('FAIL: committed=%d fresh=%d' % (len(committed_entries), len(fresh)))
|
||||
for e in only_committed[:10]:
|
||||
print(' only in manifest:', e)
|
||||
for e in only_fresh[:10]:
|
||||
print(' only on disk:', e)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent verifier for the sol working-tree triage 2026-11-03.
|
||||
|
||||
Re-derives the scope from git (untracked + 1 modified file, excluding
|
||||
wizard-work/sol/ and directory entries), re-derives every category from the
|
||||
extension rubric, confirms every frozen path exists as a real non-ignored
|
||||
file, and cross-checks aggregate counts against the committed manifest.
|
||||
|
||||
Exit 0 = artifact matches a fresh re-derivation; 1 = mismatch; 2 = misuse.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/root/troupe-standup")
|
||||
SRC = REPO / "wizard-work" / "sol"
|
||||
MANIFEST = SRC / "sol-workingtree-triage-2026-11-03.json"
|
||||
|
||||
RULER = {
|
||||
"md": "text", "txt": "text", "rst": "text", "adoc": "text", "org": "text", "log": "text",
|
||||
"json": "data", "csv": "data", "tsv": "data", "yaml": "data", "yml": "data", "toml": "data",
|
||||
"ipynb": "data",
|
||||
"mp3": "media", "wav": "media", "mp4": "media", "webm": "media", "png": "media",
|
||||
"jpg": "media", "jpeg": "media", "gif": "media", "webp": "media", "svg": "media",
|
||||
"py": "script", "sh": "script", "bash": "script", "zsh": "script", "ps1": "script", "bat": "script",
|
||||
"pdf": "doc", "doc": "doc", "docx": "doc", "ppt": "doc", "pptx": "doc", "xls": "doc", "xlsx": "doc",
|
||||
}
|
||||
|
||||
|
||||
def sh(args):
|
||||
return subprocess.run(args, cwd=REPO, check=True, capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
def re_derive_scope():
|
||||
out = set()
|
||||
for raw in sh(["git", "ls-files", "--others", "--exclude-standard"]).splitlines():
|
||||
rel = raw.rstrip("\n").rstrip("\r")
|
||||
if not rel or rel.endswith("/"):
|
||||
continue
|
||||
if rel.startswith("wizard-work/sol/"):
|
||||
continue
|
||||
out.add(rel)
|
||||
for raw in sh(["git", "diff", "--name-only"]).splitlines():
|
||||
rel = raw.rstrip("\n").rstrip("\r")
|
||||
if rel and not rel.startswith("wizard-work/sol/"):
|
||||
out.add(rel)
|
||||
return out
|
||||
|
||||
|
||||
def classify(rel):
|
||||
name = rel.rsplit("/", 1)[-1]
|
||||
ext = name.rsplit(".", 1)[1].lower() if "." in name else ""
|
||||
if ext in RULER:
|
||||
return RULER[ext]
|
||||
p = REPO / rel
|
||||
try:
|
||||
with open(p, "rb") as fh:
|
||||
head = fh.read(1024)
|
||||
except OSError:
|
||||
return "data"
|
||||
try:
|
||||
head.decode("utf-8")
|
||||
return "text"
|
||||
except UnicodeDecodeError:
|
||||
return "data"
|
||||
|
||||
|
||||
def is_ignored(rel):
|
||||
r = subprocess.run(["git", "-C", str(REPO), "check-ignore", "-q", "--", rel],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return r.returncode == 0
|
||||
|
||||
|
||||
def main():
|
||||
if not MANIFEST.exists():
|
||||
print("FAIL: manifest missing: %s" % MANIFEST)
|
||||
return 2
|
||||
manifest = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
files = manifest.get("files", [])
|
||||
by_category = manifest.get("by_category", {})
|
||||
total = manifest.get("total_files")
|
||||
|
||||
# 1. Scope re-derivation (untracked + modified, minus sol output area).
|
||||
expected = re_derive_scope()
|
||||
found = sorted(f["rel"] for f in files)
|
||||
if set(found) != expected:
|
||||
print("FAIL: scope mismatch: manifest=%d re_derivation=%d\n missing=%s\n extra=%s"
|
||||
% (len(found), len(expected),
|
||||
sorted(set(expected) - set(found))[:10], sorted(set(found) - set(expected))[:10]))
|
||||
return 1
|
||||
|
||||
# 2. Re-derive each category; confirm existence and non-ignored.
|
||||
problems = []
|
||||
for f in files:
|
||||
rel, recorded = f["rel"], f["category"]
|
||||
exp = classify(rel)
|
||||
if exp != recorded:
|
||||
problems.append("category mismatch %s: rubric=%s manifest=%s" % (rel, exp, recorded))
|
||||
p = REPO / rel
|
||||
if not p.is_file():
|
||||
problems.append("missing on disk: %s" % rel)
|
||||
elif is_ignored(rel):
|
||||
problems.append("ignored: %s" % rel)
|
||||
|
||||
# 3. Aggregate self-consistency.
|
||||
counts = {}
|
||||
for f in files:
|
||||
counts[f["category"]] = counts.get(f["category"], 0) + 1
|
||||
if total != len(files):
|
||||
problems.append("total_files %s != manifest files %d" % (total, len(files)))
|
||||
if counts != by_category:
|
||||
problems.append("by_category mismatch: manifest=%s recomputed=%s" % (by_category, counts))
|
||||
|
||||
if problems:
|
||||
print("VERIFY FAIL (%d problems)" % len(problems))
|
||||
for pb in problems:
|
||||
print(" - %s" % pb)
|
||||
return 1
|
||||
print("VERIFY OK (%d files, scope re-derived, categories rubric re-derived, all paths present)" % len(files))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user