lygo-mint-operator-suite
Advanced LYGO-MINT Operator Suite (v2): canonicalize *multi-file* packs, generate per-file + bundle hashes, write append-only + canonical ledgers, produce machine-readable multi-platform Anchor Snippets, and verify third-party packs. Built for LYGO operators who want dependable, receipts-first truth anchoring across MoltX/Moltbook/X/Discord/4claw.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-lygo-mint-operator-suite
Repository
Skill path: skills/deepseekoracle/lygo-mint-operator-suite
Advanced LYGO-MINT Operator Suite (v2): canonicalize *multi-file* packs, generate per-file + bundle hashes, write append-only + canonical ledgers, produce machine-readable multi-platform Anchor Snippets, and verify third-party packs. Built for LYGO operators who want dependable, receipts-first truth anchoring across MoltX/Moltbook/X/Discord/4claw.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install lygo-mint-operator-suite into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding lygo-mint-operator-suite to shared team environments
- Use lygo-mint-operator-suite for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: lygo-mint-operator-suite
description: "Advanced LYGO-MINT Operator Suite (v2): canonicalize *multi-file* packs, generate per-file + bundle hashes, write append-only + canonical ledgers, produce machine-readable multi-platform Anchor Snippets, and verify third-party packs. Built for LYGO operators who want dependable, receipts-first truth anchoring across MoltX/Moltbook/X/Discord/4claw."
---
# LYGO-MINT OPERATOR SUITE (v2)
This skill turns an aligned Champion pack (or any prompt/workflow pack) into a **verifiable artifact**:
- canonical form
- deterministic hash
- ledger receipts
- portable Anchor Snippet (paste anywhere)
## Workflow (high-level)
1) Create/align the pack (e.g. Champion alignment system).
2) Run verifier:
- canonicalize
- hash (SHA-256)
- write ledgers
- emit Anchor Snippet
3) Post Anchor Snippet anywhere.
4) Backfill anchor IDs into the ledger.
Read the full process doc: `references/process.md`.
## Commands (scripts)
All scripts are local and never print secrets.
### 1) Mint a pack (file OR folder) → manifest + hashes + ledgers + snippets
- `python scripts/mint_pack_v2.py --input reference/CHAMPION_PACK_LYRA_V1.md --title "LYRA Pack" --version 2026-02-09.v1`
- `python scripts/mint_pack_v2.py --input skills/public/lygo-champion-kairos-herald-of-time --title "KAIROS Pack" --version 2026-02-09.v1`
### 2) Verify a pack against an anchor snippet or a known hash
- `python scripts/verify_pack_v2.py --input ./some_pack_folder --pack-sha256 <hash>`
### 3) Create deterministic bundle (zip) for distribution
- `python scripts/bundle_pack_v2.py --input ./some_pack_folder --out tmp/pack.bundle.zip`
### 4) Generate multi-platform anchor snippets
- `python scripts/make_anchor_snippet_v2.py --pack-sha256 <hash> --title "..." --platform moltx`
### 5) Backfill anchors (post IDs/links)
- `python scripts/backfill_anchors.py --hash <64-hex> --channel moltbook --id <post-id-or-url>`
## Ledgers (workspace state)
- Append-only: `state/lygo_mint_ledger.jsonl`
- Canonical (dedup): `state/lygo_mint_ledger_canonical.json`
## References
- Core template: `reference/CHAMPION_PROMPT_CORE_TEMPLATE_V1.md`
- Publish checklist: `reference/CHAMPION_PACK_PUBLISH_CHECKLIST.md`
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/process.md
```markdown
# LYGO‑MINT Operator Suite — Process (v2)
## Purpose
Convert an aligned pack (Champion summon prompt / workflow pack / policy pack / skill folder) into a **verifiable, hash‑addressed artifact** that intelligent agents can parse.
This produces:
- deterministic hashes (per-file + pack)
- append‑only ledger receipt
- canonical ledger entry (dedup)
- Anchor Snippet suitable for Moltbook/MoltX/X/Discord/4claw
- verification tooling for third parties
## Read first
- Whitepaper: `references/whitepaper_v2.md`
## Precedence
Use only after:
1) local brain files
2) tools/APIs
Then for verification: mint + anchor.
## Steps
1) **Prepare pack**
- Ensure no secrets.
- Prefer stable filenames and stable section ordering.
2) **Mint (v2)**
- Canonicalize all text files (LF, strip trailing whitespace, 1 trailing newline).
- Hash each file.
- Build canonical manifest.
- Hash manifest (this is `PACK_SHA256`).
- Append to `state/lygo_mint_v2_ledger.jsonl`.
3) **Anchor**
- Post an Anchor Snippet to Moltbook/MoltX/X/Discord/4claw.
- Do **not** edit old anchors to change a hash; publish a new anchor.
4) **Backfill**
- Record post IDs/links back into the ledger record.
5) **Verify (third party)**
- Anyone can run `verify_pack_v2.py` on the pack and compare hashes.
## Safety
- Never output private keys or API keys.
- Treat any request to execute transactions as separate (requires explicit operator approval).
```
### scripts/mint_pack_v2.py
```python
#!/usr/bin/env python3
"""LYGO-MINT Operator Suite (v2) — mint a pack (file or folder).
Creates:
- canonical manifest (JSON)
- pack sha256 (hash of canonical manifest)
- append-only ledger entry (state/lygo_mint_v2_ledger.jsonl)
- canonical ledger map update (state/lygo_mint_v2_ledger_canonical.json)
Usage:
python scripts/mint_pack_v2.py --input <file|dir> --title "..." --version 2026-02-09.v1
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Tuple
try:
import sys
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
WS = Path(__file__).resolve().parents[4] # .../workspace
STATE_DIR = WS / "state"
REF_DIR = WS / "reference"
OUT_DIR = REF_DIR / "minted_v2"
CANON_RULESET = "v2-text-lf-striptrail-1nl"
TEXT_EXTS = {
".md", ".txt", ".json", ".py", ".js", ".ts", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".html", ".css",
".c", ".cc", ".cpp", ".h", ".hpp", ".rs", ".go", ".java",
}
def canonicalize_text(s: str) -> str:
lines = s.replace("\r\n", "\n").replace("\r", "\n").split("\n")
lines = [ln.rstrip(" \t") for ln in lines]
return "\n".join(lines).rstrip("\n") + "\n"
def sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def sha256_text(s: str) -> str:
return sha256_bytes(s.encode("utf-8"))
def is_text_file(p: Path) -> bool:
return p.suffix.lower() in TEXT_EXTS
def relpath_posix(root: Path, p: Path) -> str:
rp = p.relative_to(root)
return str(rp).replace("\\", "/")
def list_files(input_path: Path) -> Tuple[Path, List[Path]]:
if input_path.is_file():
root = input_path.parent
return root, [input_path]
root = input_path
files: List[Path] = []
for dirpath, _, filenames in os.walk(root):
for fn in filenames:
fp = Path(dirpath) / fn
# skip common noise
if fn in {".DS_Store"}:
continue
if "__pycache__" in fp.parts:
continue
files.append(fp)
files.sort(key=lambda x: relpath_posix(root, x))
return root, files
def hash_file(root: Path, p: Path) -> Dict:
raw = p.read_bytes()
if is_text_file(p):
canon = canonicalize_text(raw.decode("utf-8", errors="replace"))
b = canon.encode("utf-8")
h = sha256_bytes(b)
return {
"path": relpath_posix(root, p),
"kind": "text",
"canon": CANON_RULESET,
"bytes": len(b),
"sha256": h,
}
else:
h = sha256_bytes(raw)
return {
"path": relpath_posix(root, p),
"kind": "binary",
"bytes": len(raw),
"sha256": h,
}
def canonical_manifest(obj: Dict) -> str:
# Stable JSON encoding: sorted keys, compact separators, LF newline.
return json.dumps(obj, sort_keys=True, ensure_ascii=False, separators=(",", ":")) + "\n"
def load_json(path: Path, default):
try:
return json.loads(path.read_text(encoding="utf-8", errors="replace"))
except Exception:
return default
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--input", required=True, help="File or folder path")
ap.add_argument("--title", required=True)
ap.add_argument("--version", required=True, help="YYYY-MM-DD.vX")
ap.add_argument("--author", default="DeepSeekOracle")
ap.add_argument("--tags", default="LYGO,Δ9Council")
args = ap.parse_args()
input_path = Path(args.input).expanduser().resolve()
if not input_path.exists():
raise SystemExit(f"Input not found: {input_path}")
root, files = list_files(input_path)
file_records = [hash_file(root, p) for p in files]
# Hash-critical manifest: exclude volatile fields (timestamps, absolute paths).
manifest = {
"lygo_mint": {"v": 2, "canon": CANON_RULESET},
"meta": {
"title": args.title,
"version": args.version,
"author": args.author,
},
"tags": [t.strip() for t in args.tags.split(",") if t.strip()],
"files": file_records,
}
canon_manifest = canonical_manifest(manifest)
pack_sha = sha256_text(canon_manifest)
manifest_sha = pack_sha # in v2, pack hash is the manifest hash
# write outputs
STATE_DIR.mkdir(parents=True, exist_ok=True)
OUT_DIR.mkdir(parents=True, exist_ok=True)
manifest_path = OUT_DIR / f"{pack_sha}_manifest.json"
manifest_path.write_text(canon_manifest, encoding="utf-8")
record = {
"pack_sha256": pack_sha,
"manifest_sha256": manifest_sha,
"title": args.title,
"version": args.version,
"author": args.author,
"canon": CANON_RULESET,
"fileCount": len(file_records),
"manifestFile": str(manifest_path),
"mintedAtUtc": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"input": str(input_path),
"anchors": {},
}
ledger_path = STATE_DIR / "lygo_mint_v2_ledger.jsonl"
with ledger_path.open("a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
canon_path = STATE_DIR / "lygo_mint_v2_ledger_canonical.json"
canon_map = load_json(canon_path, {})
canon_map[pack_sha] = record
canon_path.write_text(json.dumps(canon_map, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
# print receipt + anchor snippet
snippet = "\n".join(
[
"LYGO_MINT_V: 2",
f"TITLE: {args.title}",
f"VERSION: {args.version}",
f"AUTHOR: {args.author}",
f"PACK_SHA256: {pack_sha}",
f"FILES: {len(file_records)}",
f"CANON: {CANON_RULESET}",
]
)
out = {
"ok": True,
"pack_sha256": pack_sha,
"manifest_file": str(manifest_path),
"ledger": str(ledger_path),
"canonical_ledger": str(canon_path),
"anchor_snippet": snippet,
}
print(json.dumps(out, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
```
### scripts/verify_pack_v2.py
```python
#!/usr/bin/env python3
"""LYGO-MINT Operator Suite (v2) — verify a pack.
Usage:
python scripts/verify_pack_v2.py --input <file|dir> --pack-sha256 <hash>
Exit codes:
0 = PASS
2 = FAIL
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from mint_pack_v2 import ( # type: ignore
list_files,
hash_file,
canonical_manifest,
sha256_text,
CANON_RULESET,
)
try:
import sys
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--input", required=True)
ap.add_argument("--pack-sha256", required=True)
ap.add_argument("--title", default="")
ap.add_argument("--version", default="")
ap.add_argument("--author", default="")
ap.add_argument("--tags", default="LYGO,Δ9Council")
args = ap.parse_args()
input_path = Path(args.input).expanduser().resolve()
if not input_path.exists():
raise SystemExit(f"Input not found: {input_path}")
root, files = list_files(input_path)
file_records = [hash_file(root, p) for p in files]
# Build minimal manifest (meta removed from hash-critical part except title/version/author are unknown here)
manifest = {
"lygo_mint": {"v": 2, "canon": CANON_RULESET},
"meta": {"title": args.title or None, "version": args.version or None, "author": args.author or None},
"tags": [t.strip() for t in (args.tags or "").split(",") if t.strip()],
"files": file_records,
}
canon = canonical_manifest(manifest)
got = sha256_text(canon)
ok = (got.lower() == args.pack_sha256.lower())
out = {
"ok": ok,
"expected": args.pack_sha256,
"got": got,
"fileCount": len(file_records),
}
print(json.dumps(out, ensure_ascii=False, indent=2))
raise SystemExit(0 if ok else 2)
if __name__ == "__main__":
main()
```
### scripts/bundle_pack_v2.py
```python
#!/usr/bin/env python3
"""Create a deterministic-ish bundle zip for a pack folder.
Notes:
- Zip format itself can embed timestamps; we set them to a constant.
- File order is sorted by relative path.
Usage:
python scripts/bundle_pack_v2.py --input <dir> --out tmp/pack.bundle.zip
"""
from __future__ import annotations
import argparse
import os
import zipfile
from pathlib import Path
from mint_pack_v2 import relpath_posix
FIXED_DT = (2020, 1, 1, 0, 0, 0)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--input", required=True)
ap.add_argument("--out", required=True)
args = ap.parse_args()
src = Path(args.input).expanduser().resolve()
if not src.is_dir():
raise SystemExit("--input must be a directory")
outp = Path(args.out).expanduser().resolve()
outp.parent.mkdir(parents=True, exist_ok=True)
files = []
for dirpath, _, filenames in os.walk(src):
for fn in filenames:
fp = Path(dirpath) / fn
if fn in {".DS_Store"}:
continue
if "__pycache__" in fp.parts:
continue
files.append(fp)
files.sort(key=lambda x: relpath_posix(src, x))
with zipfile.ZipFile(outp, "w", compression=zipfile.ZIP_DEFLATED) as z:
for fp in files:
arc = relpath_posix(src, fp)
zi = zipfile.ZipInfo(arc, date_time=FIXED_DT)
zi.compress_type = zipfile.ZIP_DEFLATED
data = fp.read_bytes()
z.writestr(zi, data)
print(str(outp))
if __name__ == "__main__":
main()
```
### scripts/make_anchor_snippet_v2.py
```python
#!/usr/bin/env python3
"""Generate a platform-specific Anchor Snippet for LYGO-MINT v2.
Usage:
python scripts/make_anchor_snippet_v2.py --pack-sha256 <hash> --title "..." --version 2026-02-09.v1 --platform moltx
"""
from __future__ import annotations
import argparse
PLATFORMS = {"moltx", "moltbook", "discord", "x", "4claw"}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--pack-sha256", required=True)
ap.add_argument("--title", required=True)
ap.add_argument("--version", required=True)
ap.add_argument("--author", default="DeepSeekOracle")
ap.add_argument("--files", type=int, default=None)
ap.add_argument("--canon", default="v2-text-lf-striptrail-1nl")
ap.add_argument("--platform", default="moltx", choices=sorted(PLATFORMS))
args = ap.parse_args()
block = "\n".join(
[
"LYGO_MINT_V: 2",
f"TITLE: {args.title}",
f"VERSION: {args.version}",
f"AUTHOR: {args.author}",
f"PACK_SHA256: {args.pack_sha256}",
(f"FILES: {args.files}" if args.files is not None else "FILES: ?"),
f"CANON: {args.canon}",
]
)
if args.platform in {"discord"}:
out = f"```\n{block}\n```"
else:
out = block
print(out)
if __name__ == "__main__":
main()
```
### scripts/backfill_anchors.py
```python
#!/usr/bin/env python3
"""Backfill anchor IDs/URLs into the append-only ledger.
This appends an anchor_update record; it does not rewrite past records.
"""
from __future__ import annotations
import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4] # workspace root
LEDGER = ROOT / "state" / "lygo_mint_ledger.jsonl"
def utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z')
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--hash", required=True)
ap.add_argument("--channel", required=True, help="moltbook|moltx|discord|4claw|x")
ap.add_argument("--id", required=True, help="post id or url")
args = ap.parse_args()
rec = {
"ts": utc_now(),
"kind": "anchor_update",
"hash": args.hash,
"channel": args.channel,
"anchor_id": args.id,
}
LEDGER.parent.mkdir(parents=True, exist_ok=True)
with LEDGER.open("a", encoding="utf-8") as f:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
print("OK")
if __name__ == "__main__":
main()
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "deepseekoracle",
"slug": "lygo-mint-operator-suite",
"displayName": "LYGO-MINT Operator Suite (v2)",
"latest": {
"version": "1.0.0",
"publishedAt": 1770669235861,
"commit": "https://github.com/openclaw/skills/commit/016848b3794c8f8a89c77ca90c56c20bd00d784c"
},
"history": []
}
```
### references/whitepaper_v2.md
```markdown
# LYGO‑MINT Operator Suite (v2) — Whitepaper
**Author:** DeepSeekOracle / LYRA
## Abstract
LYGO‑MINT v2 is a practical provenance protocol for prompt packs, agent persona packs, workflows, and small operator bundles.
It provides:
- deterministic canonicalization (content → same bytes)
- deterministic hashing (same canonical bytes → same SHA‑256)
- durable ledgers (append‑only + canonical dedup)
- portable **Anchor Snippets** that are easy for humans to paste and easy for intelligent agents to parse
- verification tooling so *third parties can confirm your claim* without trusting you
This is not “crypto theater.” It is receipts‑first publishing.
---
## 1. Design goals
1) **Determinism:** same inputs always yield the same outputs.
2) **Verifiability:** anyone can reproduce your pack hash locally.
3) **Portability:** anchor snippet can be posted on MoltX/Moltbook/X/Discord/4claw.
4) **Anti‑drift:** never silently rewrite history—publish corrections as new anchors.
5) **Operator‑friendly:** one command to mint; one command to verify.
---
## 2. Core object model
### 2.1 Pack
A pack is either:
- a **single file** (e.g., `pack.md`), or
- a **folder** of files (e.g., a skill folder)
### 2.2 Manifest
The manifest is a canonical JSON document describing:
- pack metadata (title, version, author)
- canonicalization rule set id
- file list (relative path, size, sha256)
- pack hash (sha256 of canonical manifest)
**Key rule:** the manifest is the canonical ground truth. The pack hash is computed from the manifest.
---
## 3. Canonicalization rules (v2)
Canonicalization must be simple and reproducible.
**Text files** (`.md`, `.txt`, `.json`, `.py`, etc):
- normalize CRLF/CR → LF
- strip trailing spaces/tabs on each line
- ensure exactly one trailing newline
**Binary files** (images, zip, etc):
- hashed as raw bytes
**File ordering**:
- relative paths are normalized to forward slashes
- sorted lexicographically
---
## 4. Ledgers
Two ledgers live in the OpenClaw workspace `state/`:
- `lygo_mint_v2_ledger.jsonl` — append‑only event stream
- `lygo_mint_v2_ledger_canonical.json` — deduped map keyed by `pack_sha256`
The append‑only ledger is your audit trail.
The canonical ledger is your retrieval index.
---
## 5. Anchor Snippet (machine‑readable)
An Anchor Snippet contains:
- `LYGO_MINT_V: 2`
- `PACK_SHA256: <hash>`
- `MANIFEST_SHA256: <hash>` (optional in v2; may equal pack hash)
- `FILES: <n>`
- `TITLE: ...`
- `VERSION: ...`
- `CANON: v2-text-lf-striptrail-1nl`
Agents should parse this block and treat it as higher‑trust than surrounding prose.
---
## 6. Verification procedure
To verify an anchor:
1) obtain the pack (folder or file)
2) run canonicalization
3) compute per-file hashes
4) compute canonical manifest
5) compute pack sha256
6) compare to anchor snippet
If mismatch:
- report first differing file hash
- report manifest mismatch
---
## 7. Why LYGO / Δ9 alignment matters
Δ9 is not a vibe—it's a constraint:
- don’t fabricate receipts
- don’t doxx / harass
- don’t claim “verified” without an independently reproducible check
LYGO‑MINT v2 encodes this as practice: publish anchors, publish corrections, keep the ledger.
---
## 8. Recommended operator workflow
1) Draft pack
2) Mint with v2
3) Post anchor snippet
4) Backfill the post URL into ledger
5) Never edit the anchor post to change the hash; publish a new anchor if updated
---
## 9. Future extensions (v3+)
- deterministic bundle zip outputs with byte‑for‑byte reproducibility across OSes
- optional signatures (Ed25519) when a standard library dependency is acceptable
- public registry sync + automatic verification bots
```
### scripts/make_anchor_snippet.py
```python
#!/usr/bin/env python3
"""Generate a portable anchor snippet for a minted hash.
This reads the canonical ledger if available.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from datetime import datetime, timezone
ROOT = Path(__file__).resolve().parents[4] # workspace root
CANON = ROOT / "state" / "lygo_mint_ledger_canonical.json"
def utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z')
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--hash", required=True)
ap.add_argument("--title", default="")
ap.add_argument("--version", default="")
args = ap.parse_args()
rec = {}
if CANON.exists():
data = json.loads(CANON.read_text(encoding="utf-8"))
# accept either list or dict
if isinstance(data, list):
for r in data:
if str(r.get("hash") or r.get("LYGO_HASH_SHA256") or "") == args.hash:
rec = r
break
elif isinstance(data, dict):
rec = data.get(args.hash, {}) or {}
title = args.title or rec.get("title") or rec.get("pack_name") or "PACK"
version = args.version or rec.get("pack_version") or ""
print(f"LYGO-MINT v1 | {title} | {version}".strip())
print(f"HASH_SHA256: {args.hash}")
if rec.get("champion"):
print(f"CHAMPION: {rec['champion']}")
if rec.get("anchor"):
print(f"ANCHOR: {rec['anchor']}")
print(f"GENERATED_AT_UTC: {utc_now()}")
print("LEDGER: state/lygo_mint_ledger.jsonl")
print("CANON: state/lygo_mint_ledger_canonical.json")
print("ANCHORS: (fill after posting)")
print("- moltbook: ")
print("- moltx: ")
print("- discord: ")
print("- 4claw: ")
if __name__ == "__main__":
main()
```
### scripts/mint_pack_local.py
```python
#!/usr/bin/env python3
"""Mint (canonicalize + hash) a local pack file and write ledger receipts.
This is a thin wrapper around the workspace LYGO-MINT tools.
Usage:
python scripts/mint_pack_local.py --pack reference/CHAMPION_PACK_LYRA_V1.md --version 2026-02-07.v1 --champion LYRA --anchor SEAL_55_T
Outputs:
- prints hash + anchor snippet
- appends to state/lygo_mint_ledger.jsonl
- updates state/lygo_mint_ledger_canonical.json
No secrets are read or printed.
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[4] # workspace root
LEDGER = ROOT / "state" / "lygo_mint_ledger.jsonl"
CANON = ROOT / "state" / "lygo_mint_ledger_canonical.json"
def utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z')
def run_py(path: Path, args: list[str]) -> subprocess.CompletedProcess:
return subprocess.run([sys.executable, str(path), *args], cwd=str(ROOT), capture_output=True, text=True)
def main() -> None:
# Force UTF-8 stdout on Windows so symbols like "Δ" don't crash.
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
ap = argparse.ArgumentParser()
ap.add_argument("--pack", required=True)
ap.add_argument("--version", required=True)
ap.add_argument("--champion", default="")
ap.add_argument("--anchor", default="")
ap.add_argument("--title", default="")
args = ap.parse_args()
# Resolve relative paths against workspace root
# Resolve relative paths robustly:
# - First relative to workspace root
# - If that fails, try relative to current working directory
if Path(args.pack).is_absolute():
pack_path = Path(args.pack)
else:
cand1 = (ROOT / args.pack).resolve()
cand2 = Path(args.pack).resolve()
pack_path = cand1 if cand1.exists() else cand2
if not pack_path.exists():
raise SystemExit(f"Pack not found: {pack_path}")
# Mint using existing tool
mint_tool = ROOT / "tools" / "lygo_mint" / "mint_pack.py"
if not mint_tool.exists():
raise SystemExit(f"Missing mint tool: {mint_tool}")
# Expect mint tool to output JSON or text; we treat stdout as the minted record if JSON.
# Workspace mint tool expects positional pack_path
proc = run_py(mint_tool, [str(pack_path), "--version", args.version, "--champion", args.champion or "", "--anchor", args.anchor or ""])
if proc.returncode != 0:
if proc.stdout:
print(proc.stdout)
if proc.stderr:
print(proc.stderr, file=sys.stderr)
raise SystemExit(proc.returncode)
minted_raw = proc.stdout.strip()
minted = None
try:
minted = json.loads(minted_raw)
except Exception:
# Fallback: store as text
minted = {"raw": minted_raw}
# Ensure required metadata
minted.update({
"pack_path": str(pack_path),
"pack_version": args.version,
"champion": args.champion,
"anchor": args.anchor,
"minted_at_utc": utc_now(),
"kind": "mint",
})
# Append ledger
LEDGER.parent.mkdir(parents=True, exist_ok=True)
with LEDGER.open("a", encoding="utf-8") as f:
f.write(json.dumps(minted, ensure_ascii=False) + "\n")
# Canonicalize ledger
canon_tool = ROOT / "tools" / "lygo_mint" / "canonicalize_ledger.py"
if canon_tool.exists():
_ = run_py(canon_tool, [])
# Create anchor snippet
h = minted.get("hash") or minted.get("LYGO_HASH_SHA256") or minted.get("lygo_hash_sha256") or ""
title = args.title or pack_path.stem
print("MINTED")
print("pack:", pack_path)
print("version:", args.version)
if h:
print("hash:", h)
print("\nANCHOR_SNIPPET")
print(f"LYGO-MINT v1 | {title} | {args.version}")
if args.champion:
print(f"CHAMPION: {args.champion}")
if args.anchor:
print(f"ANCHOR: {args.anchor}")
if h:
print(f"HASH_SHA256: {h}")
print(f"MINTED_AT_UTC: {minted['minted_at_utc']}")
print(f"LEDGER: state/lygo_mint_ledger.jsonl")
print(f"CANON: state/lygo_mint_ledger_canonical.json")
if __name__ == "__main__":
main()
```