Product Spec Harness
HomeTrang chủ GuidesHướng dẫn DeterminismTất định
For developers releasing the bundleCho lập trình viên phát hành bundle

DeterminismTính tất định

Same inputs → byte-identical outputs, every time. The release skill builds a tar.gz whose bytes are reproducible across machines, across time, and across operators. Determinism is not a nice-to-have: it is what makes a SHA-256 sidecar meaningful, makes CI artifacts auditable, and lets any reviewer verify the bundle without re-running the build.Cùng đầu vào → đầu ra giống nhau từng byte, mọi lần. Skill release tạo ra một tar.gz có thể tái tạo trên mọi máy, qua mọi thời điểm, bởi mọi người vận hành. Tính tất định không phải thứ tốt có cũng được: nó làm cho sidecar SHA-256 có ý nghĩa, làm cho artifact CI kiểm toán được, và cho phép bất kỳ người đánh giá nào xác minh bundle mà không cần chạy lại build.

THE CASE FOR ITTẠI SAO CẦN

Why determinismTại sao cần tính tất định

A non-deterministic build produces a different tarball every run even if nothing changed. That makes the SHA-256 useless — it changes with every build — and forces reviewers to re-run the builder to verify the artifact. Three concrete benefits flow directly from determinism:Một build không tất định sẽ tạo ra tarball khác nhau mỗi lần chạy dù không có gì thay đổi. Điều đó làm SHA-256 vô nghĩa — nó thay đổi theo mỗi build — và buộc người đánh giá phải chạy lại builder để xác minh artifact. Ba lợi ích cụ thể đến trực tiếp từ tính tất định:

Reproducible packsGói tái tạo được

Build the same manifest on two different machines or six months apart — you get the identical tar.gz. No "it worked on my machine" for release artifacts.Build cùng manifest trên hai máy khác nhau hoặc cách nhau sáu tháng — bạn nhận được tar.gz giống hệt. Không còn "chạy được trên máy tôi" với release artifact.

AuditabilityKhả năng kiểm toán

The .sha256 sidecar is a commitment: anyone can sha256sum -c the downloaded artifact against the published hash and know it was not tampered with in transit or storage.Sidecar .sha256 là một cam kết: bất kỳ ai cũng có thể sha256sum -c artifact đã tải về với hash đã công bố để biết nó không bị can thiệp trong quá trình truyền hoặc lưu trữ.

Trust without re-runningTin cậy không cần chạy lại

A CI artifact published with its SHA-256 can be verified offline. The reviewer does not need the source repo, the venv, or build tooling — just sha256sum.Một CI artifact được công bố kèm SHA-256 có thể được xác minh offline. Người đánh giá không cần repo nguồn, venv, hay công cụ build — chỉ cần sha256sum.

PRINCIPLE 3NGUYÊN TẮC 3

Scripts do the structural workScript làm phần cấu trúc

The five operating principles include one that is especially load-bearing here: Script vs LLM split — scripts handle graph and structure; the LLM handles prose and alignment judgment. Scripts are deterministic by construction; LLM outputs are not. Keeping structural decisions in scripts keeps the reproducible path clean.Năm nguyên tắc vận hành có một điều đặc biệt quan trọng ở đây: Phân tách Script vs LLM — script xử lý đồ thị và cấu trúc; LLM xử lý văn xuôi và nhận định. Script tất định theo bản chất; đầu ra LLM thì không. Giữ quyết định cấu trúc trong script giữ cho đường tái tạo sạch.

Script — deterministicScript — tất định

Manifest loading, path walking, safety-filter evaluation, tarball assembly, SHA-256 computation, CHANGELOG section locking. Same inputs → same outputs. No variance.Nạp manifest, duyệt đường dẫn, đánh giá safety filter, ráp tarball, tính SHA-256, khóa section CHANGELOG. Cùng đầu vào → cùng đầu ra. Không có biến thiên.

manifest_loader.py safety_check.py pack/tarball release.py

LLM — judgment onlyLLM — chỉ nhận định

The release lifecycle interview — confirming the version string, reading back the [Unreleased] body, the tag-push handoff. Opinion and prose only; never the bytes of the artifact itself.Phỏng vấn vòng đời phát hành — xác nhận chuỗi version, đọc lại nội dung [Unreleased], bàn giao lệnh push tag. Chỉ nhận định và văn xuôi; không bao giờ là các byte của artifact.

release interviewphỏng vấn phát hành version confirmxác nhận version changelog reviewxem xét changelog
🔑
The pack builder (python -m pack) emits JSON on --json. The analytical scripts (manifest_loader.py, safety_check.py, build_manifest.py) emit JSON to stdout unconditionally. They always exit 0. Determinism is the script's job; the --strict gate is the LLM's job.Pack builder (python -m pack) phát JSON với --json. Các script phân tích (manifest_loader.py, safety_check.py, build_manifest.py) phát JSON ra stdout vô điều kiện. Luôn exit 0. Tính tất định là việc của script; gate --strict là việc của LLM.
HOW IT WORKSCÁCH HOẠT ĐỘNG

The deterministic packGói tất định

The release skill packs a manifest-selected subset of .claude/ into a versioned, hashed tar.gz. Every choice in the builder — format, sort order, timestamps, ownership — is made to eliminate variance. --dry-run --compute-sha lists the would-be file set and the exact SHA-256 without writing a tarball. Run it twice; get the same hash.Skill release đóng gói tập con được chọn bởi manifest của .claude/ thành một tar.gz có version và hash. Mọi lựa chọn trong builder — định dạng, thứ tự sắp xếp, timestamps, ownership — đều được thực hiện để loại bỏ biến thiên. --dry-run --compute-sha liệt kê bộ file dự kiến và SHA-256 chính xác mà không ghi tarball. Chạy hai lần; nhận cùng một hash.

python -m pack --root . --dry-run --compute-sha
bundle_root: product-spec-2.4.0
file_count: 318
total_bytes: 4626012
output_path: /home/hieubt15/Documents/vsf/product-spec/.claude/skills/release/scripts/dist/product-spec-2.4.0.tar.gz
max_size: 104857600
would_be_sha256: 414c2cbac29472cdba8f8e4a95f4cb784be6ad092b730844ba7ba806dbc8ba8f
over_max_size: False

The output above is real captured stdout. The MANIFEST.json inside the tarball carries the per-file SHA-256 of every included entry — enabling file-level integrity checks on the recipient side.Đầu ra trên là stdout thật đã chụp. MANIFEST.json bên trong tarball chứa SHA-256 từng file của mọi mục được đưa vào — cho phép kiểm tra toàn vẹn ở cấp độ file phía người nhận.

What makes it byte-identicalĐiều gì làm cho nó giống nhau từng byte

PAX formatđịnh dạng mtime=0 uid=gid=0 gzip headergzip header mtime=0 sorted file walkduyệt file đã sắp SHA-256 sidecarsidecar per-file SHA-256 inSHA-256 từng file trong MANIFEST.json

The --source-date-epoch flag overrides the mtime root (default 0). The default of 0 is what gives you full determinism regardless of when the build runs. Pass --source-date-epoch env to read SOURCE_DATE_EPOCH from the environment — the Reproducible Builds standard.Cờ --source-date-epoch ghi đè gốc mtime (mặc định 0). Mặc định 0 là thứ cho bạn tính tất định hoàn toàn bất kể build chạy lúc nào. Truyền --source-date-epoch env để đọc SOURCE_DATE_EPOCH từ môi trường — tiêu chuẩn Reproducible Builds.

Output layoutCấu trúc đầu ra

Two files land in dist/ after a real pack:Hai file xuất hiện trong dist/ sau một lần pack thật:

product-spec-{version}.tar.gz product-spec-{version}.tar.gz.sha256

Inside the tarball: MANIFEST.json, INSTALL.md, install.sh, install.ps1, and the selected .claude/ subtree.Bên trong tarball: MANIFEST.json, INSTALL.md, install.sh, install.ps1, và cây con .claude/ đã chọn.

Verify the downloadXác minh file đã tải

The sidecar is a coreutils-format file — the standard tool works directly:Sidecar là file định dạng coreutils — công cụ chuẩn dùng trực tiếp được:

sha256sum -c product-spec-2.4.1.tar.gz.sha256
product-spec-2.4.1.tar.gz: OK
NON-REMOVABLE FILTERBỘ LỌC KHÔNG THỂ TẮT

The safety filterBộ lọc an toàn

Every candidate path passes through safety_check.py before entering the tarball. The filter has three layers of always-drop rules and an opt-in catalog. No CLI flag, no manifest field, no override disables these rules. Secrets, VCS metadata, build artifacts, and runtime state are never shipped — even if explicitly listed in the manifest.Mọi đường dẫn ứng viên đều qua safety_check.py trước khi vào tarball. Bộ lọc có ba lớp quy tắc always-drop và một catalog opt-in. Không có cờ CLI, trường manifest, hay override nào vô hiệu hóa các quy tắc này. Secret, metadata VCS, build artifact, và state runtime không bao giờ được đóng gói — ngay cả khi được liệt kê rõ ràng trong manifest.

Always-drop — three layersAlways-drop — ba lớp

Layer 1 (exact basename, case-insensitive): .env, .envrc, SSH private keys (id_rsa, id_ed25519, …), .netrc, .pgpass, metadata.json, OS junk (.DS_Store, Thumbs.db).Lớp 1 (basename chính xác, không phân biệt hoa thường): .env, .envrc, SSH private key (id_rsa, id_ed25519, …), .netrc, .pgpass, metadata.json, rác OS (.DS_Store, Thumbs.db).

Layer 2 (any path component, case-insensitive): .git, __pycache__, .venv, node_modules, session-state, agent-memory, plans, dist, build.Lớp 2 (bất kỳ thành phần đường dẫn nào, không phân biệt hoa thường): .git, __pycache__, .venv, node_modules, session-state, agent-memory, plans, dist, build.

Layer 3 (glob patterns): **/.env.*, **/secrets.*, **/credentials.*, **/*.pem, **/*.key, **/*token*.json, **/*secret*.json, Python bytecode.Lớp 3 (glob pattern): **/.env.*, **/secrets.*, **/credentials.*, **/*.pem, **/*.key, **/*token*.json, **/*secret*.json, Python bytecode.

Opt-in pathsĐường dẫn opt-in

Two paths only ship when explicitly requested via flag or manifest field — they are not included by default even if discovered in .claude/:Hai đường dẫn chỉ được đóng gói khi yêu cầu tường minh qua cờ hoặc trường manifest — không được bao gồm mặc định dù có trong .claude/:

.claude/settings.json--include-settings .claude/.ck.json--include-ck-config

Shared skill dependencies (_shared/) emit a warning if referenced but not included; pass --include-shared <name> to bundle them. --strict treats an un-included _shared/ reference as an error (exit 2).Dependency skill dùng chung (_shared/) phát cảnh báo nếu được tham chiếu nhưng không được bao gồm; truyền --include-shared <name> để đóng gói cùng. --strict coi tham chiếu _shared/ chưa được bao gồm là lỗi (exit 2).

The safety filter is non-removable. No flag pulls a dropped file back into a bundle. telemetry/*.jsonl, __tests__, session state, and runtime caches are always excluded.Bộ lọc an toàn không thể tắt. Không cờ nào kéo file bị loại trở lại vào bundle. telemetry/*.jsonl, __tests__, session state, và cache runtime luôn bị loại.

For the complete always-drop catalog and the rationale behind each rule, see release. The validation page covers how the structural checks fit into the broader CI pipeline — see validation.Để xem catalog always-drop đầy đủ và lý do đằng sau mỗi quy tắc, xem release. Trang validation trình bày cách các kiểm tra cấu trúc khớp với pipeline CI rộng hơn — xem validation.

COMMON QUESTIONSCÂU HỎI THƯỜNG GẶP

FAQHỏi đáp

I built the bundle twice and got different SHAs. What went wrong?Tôi build bundle hai lần và nhận được SHA khác nhau. Chuyện gì sai?
The most common cause is a non-zero --source-date-epoch or an environment variable overriding it. The default is 0 which is fully deterministic. Also check that the manifest has not changed between runs and that no file in the selected set was modified (changed content → changed hash, by design).Nguyên nhân phổ biến nhất là --source-date-epoch khác không hoặc một biến môi trường ghi đè nó. Mặc định là 0 cho tính tất định hoàn toàn. Cũng kiểm tra xem manifest có thay đổi giữa các lần chạy không và không có file nào trong bộ đã chọn bị sửa đổi (nội dung thay đổi → hash thay đổi, theo thiết kế).
Can I add a file to the safety filter's always-drop list?Tôi có thể thêm file vào danh sách always-drop của safety filter không?
Yes — add to the appropriate layer in safety_check.py, add a row to references/safety-rules.md with the rationale, add a pytest case under scripts/tests/test_safety_check.py, and bump the CHANGELOG. The process ensures the catalog is never expanded silently.Có — thêm vào lớp phù hợp trong safety_check.py, thêm một hàng vào references/safety-rules.md kèm lý do, thêm một test pytest trong scripts/tests/test_safety_check.py, và bump CHANGELOG. Quy trình này đảm bảo catalog không bao giờ được mở rộng âm thầm.
The pack builder is deterministic — but what about the release lifecycle?Pack builder tất định — nhưng còn vòng đời phát hành thì sao?
release.py is also deterministic: it never derives content from git, so the committed CHANGELOG.md is reproducible. The GitHub Release body is the locked CHANGELOG section, extractable with release.py --extract X.Y.Z by anyone with the repo. The tag push is owner-owned and never auto-executed.release.py cũng tất định: nó không bao giờ lấy nội dung từ git, nên CHANGELOG.md đã commit có thể tái tạo được. Nội dung GitHub Release là section CHANGELOG đã khóa, có thể trích xuất bằng release.py --extract X.Y.Z bởi bất kỳ ai có repo. Lệnh push tag là của chủ sở hữu và không bao giờ được tự động thực thi.
Does --dry-run actually compute the SHA, or just list files?--dry-run có thực sự tính SHA không, hay chỉ liệt kê file?
By default, --dry-run lists the would-be file set and total size without writing a tarball. Add --compute-sha to also compute the would-be SHA-256 — it requires a double-pass so it is opt-in. The result is the exact same hash you would get from a real pack run with the same inputs.Mặc định, --dry-run liệt kê bộ file dự kiến và tổng kích thước mà không ghi tarball. Thêm --compute-sha để cũng tính SHA-256 dự kiến — cần double-pass nên là opt-in. Kết quả là cùng hash chính xác mà bạn nhận được từ một lần pack thật với cùng đầu vào.