entviz 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- entviz-0.10.0/.claude/skills/review-board/SKILL.md +107 -0
- entviz-0.10.0/.claude/workflows/review-panel.js +417 -0
- entviz-0.10.0/.github/copilot-instructions.md +11 -0
- entviz-0.10.0/.github/dependabot.yml +25 -0
- entviz-0.10.0/.github/rulesets/README.md +59 -0
- entviz-0.10.0/.github/rulesets/apply.sh +31 -0
- entviz-0.10.0/.github/rulesets/protect-release-tags.json +28 -0
- entviz-0.10.0/.github/rulesets/require-review-contributors.json +69 -0
- entviz-0.10.0/.github/workflows/ci.yml +64 -0
- entviz-0.10.0/.github/workflows/deploy-docs.yml +90 -0
- entviz-0.10.0/.github/workflows/release.yml +135 -0
- entviz-0.10.0/.gitignore +159 -0
- entviz-0.10.0/AGENTS.md +112 -0
- entviz-0.10.0/CLAUDE.md +3 -0
- entviz-0.10.0/GEMINI.md +4 -0
- entviz-0.10.0/LICENSE +201 -0
- entviz-0.10.0/PKG-INFO +177 -0
- entviz-0.10.0/README.md +151 -0
- entviz-0.10.0/SECURITY.md +56 -0
- entviz-0.10.0/TODO.md +53 -0
- entviz-0.10.0/compliance/README.md +65 -0
- entviz-0.10.0/compliance/__init__.py +23 -0
- entviz-0.10.0/compliance/corpus/ar-1x1/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ar-1x1/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ar-1x1/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ar-1x1/model.json +222 -0
- entviz-0.10.0/compliance/corpus/ar-1x2/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ar-1x2/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ar-1x2/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ar-1x2/model.json +222 -0
- entviz-0.10.0/compliance/corpus/ar-2x1/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ar-2x1/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ar-2x1/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ar-2x1/model.json +222 -0
- entviz-0.10.0/compliance/corpus/avalanche-a/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/avalanche-a/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/avalanche-a/input.json +10 -0
- entviz-0.10.0/compliance/corpus/avalanche-a/model.json +136 -0
- entviz-0.10.0/compliance/corpus/avalanche-b/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/avalanche-b/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/avalanche-b/input.json +10 -0
- entviz-0.10.0/compliance/corpus/avalanche-b/model.json +136 -0
- entviz-0.10.0/compliance/corpus/b64-large/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/b64-large/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/b64-large/input.json +10 -0
- entviz-0.10.0/compliance/corpus/b64-large/model.json +372 -0
- entviz-0.10.0/compliance/corpus/bitcoincash/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/bitcoincash/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/bitcoincash/input.json +10 -0
- entviz-0.10.0/compliance/corpus/bitcoincash/model.json +222 -0
- entviz-0.10.0/compliance/corpus/btc-legacy/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/btc-legacy/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/btc-legacy/input.json +10 -0
- entviz-0.10.0/compliance/corpus/btc-legacy/model.json +180 -0
- entviz-0.10.0/compliance/corpus/btc-segwit/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/btc-segwit/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/btc-segwit/input.json +10 -0
- entviz-0.10.0/compliance/corpus/btc-segwit/model.json +216 -0
- entviz-0.10.0/compliance/corpus/btc-segwit-p2wsh/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/btc-segwit-p2wsh/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/btc-segwit-p2wsh/input.json +10 -0
- entviz-0.10.0/compliance/corpus/btc-segwit-p2wsh/model.json +278 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-b/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-b/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-b/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-b/model.json +222 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-d/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-d/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-d/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cesr-aid-d/model.json +222 -0
- entviz-0.10.0/compliance/corpus/cesr-said-e/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cesr-said-e/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cesr-said-e/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cesr-said-e/model.json +222 -0
- entviz-0.10.0/compliance/corpus/cid-v0/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cid-v0/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cid-v0/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cid-v0/model.json +222 -0
- entviz-0.10.0/compliance/corpus/cid-v1/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cid-v1/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cid-v1/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cid-v1/model.json +278 -0
- entviz-0.10.0/compliance/corpus/cosmos/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/cosmos/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/cosmos/input.json +10 -0
- entviz-0.10.0/compliance/corpus/cosmos/model.json +180 -0
- entviz-0.10.0/compliance/corpus/err-eip55-bad-checksum/input.json +10 -0
- entviz-0.10.0/compliance/corpus/err-fontsize-high/input.json +10 -0
- entviz-0.10.0/compliance/corpus/err-fontsize-low/input.json +10 -0
- entviz-0.10.0/compliance/corpus/err-note-punct/input.json +10 -0
- entviz-0.10.0/compliance/corpus/err-note-space/input.json +10 -0
- entviz-0.10.0/compliance/corpus/err-note-too-long/input.json +10 -0
- entviz-0.10.0/compliance/corpus/eth-checksummed/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/eth-checksummed/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/eth-checksummed/input.json +10 -0
- entviz-0.10.0/compliance/corpus/eth-checksummed/model.json +174 -0
- entviz-0.10.0/compliance/corpus/eth-lower/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/eth-lower/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/eth-lower/input.json +10 -0
- entviz-0.10.0/compliance/corpus/eth-lower/model.json +174 -0
- entviz-0.10.0/compliance/corpus/fs-12/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/fs-12/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/fs-12/input.json +10 -0
- entviz-0.10.0/compliance/corpus/fs-12/model.json +222 -0
- entviz-0.10.0/compliance/corpus/fs-24/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/fs-24/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/fs-24/input.json +10 -0
- entviz-0.10.0/compliance/corpus/fs-24/model.json +222 -0
- entviz-0.10.0/compliance/corpus/fs-6/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/fs-6/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/fs-6/input.json +10 -0
- entviz-0.10.0/compliance/corpus/fs-6/model.json +222 -0
- entviz-0.10.0/compliance/corpus/gitoid-blob-sha256/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/gitoid-blob-sha256/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/gitoid-blob-sha256/input.json +10 -0
- entviz-0.10.0/compliance/corpus/gitoid-blob-sha256/model.json +222 -0
- entviz-0.10.0/compliance/corpus/hex-1024/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/hex-1024/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/hex-1024/input.json +10 -0
- entviz-0.10.0/compliance/corpus/hex-1024/model.json +372 -0
- entviz-0.10.0/compliance/corpus/hex-128/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/hex-128/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/hex-128/input.json +10 -0
- entviz-0.10.0/compliance/corpus/hex-128/model.json +136 -0
- entviz-0.10.0/compliance/corpus/hex-256/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/hex-256/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/hex-256/input.json +10 -0
- entviz-0.10.0/compliance/corpus/hex-256/model.json +222 -0
- entviz-0.10.0/compliance/corpus/hex-512/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/hex-512/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/hex-512/input.json +10 -0
- entviz-0.10.0/compliance/corpus/hex-512/model.json +384 -0
- entviz-0.10.0/compliance/corpus/hex-64/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/hex-64/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/hex-64/input.json +10 -0
- entviz-0.10.0/compliance/corpus/hex-64/model.json +110 -0
- entviz-0.10.0/compliance/corpus/lei-bloomberg/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/lei-bloomberg/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/lei-bloomberg/input.json +10 -0
- entviz-0.10.0/compliance/corpus/lei-bloomberg/model.json +138 -0
- entviz-0.10.0/compliance/corpus/lei-lowercase/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/lei-lowercase/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/lei-lowercase/input.json +10 -0
- entviz-0.10.0/compliance/corpus/lei-lowercase/model.json +138 -0
- entviz-0.10.0/compliance/corpus/litecoin/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/litecoin/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/litecoin/input.json +10 -0
- entviz-0.10.0/compliance/corpus/litecoin/model.json +216 -0
- entviz-0.10.0/compliance/corpus/manifest.json +78 -0
- entviz-0.10.0/compliance/corpus/note-git/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/note-git/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/note-git/input.json +10 -0
- entviz-0.10.0/compliance/corpus/note-git/model.json +174 -0
- entviz-0.10.0/compliance/corpus/ripple/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ripple/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ripple/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ripple/model.json +178 -0
- entviz-0.10.0/compliance/corpus/snowflake-19/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/snowflake-19/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/snowflake-19/input.json +10 -0
- entviz-0.10.0/compliance/corpus/snowflake-19/model.json +108 -0
- entviz-0.10.0/compliance/corpus/snowflake-discord/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/snowflake-discord/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/snowflake-discord/input.json +10 -0
- entviz-0.10.0/compliance/corpus/snowflake-discord/model.json +110 -0
- entviz-0.10.0/compliance/corpus/ssh-ed25519/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ssh-ed25519/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ssh-ed25519/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ssh-ed25519/model.json +222 -0
- entviz-0.10.0/compliance/corpus/stellar/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/stellar/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/stellar/input.json +10 -0
- entviz-0.10.0/compliance/corpus/stellar/model.json +272 -0
- entviz-0.10.0/compliance/corpus/swhid-cnt/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/swhid-cnt/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/swhid-cnt/input.json +10 -0
- entviz-0.10.0/compliance/corpus/swhid-cnt/model.json +174 -0
- entviz-0.10.0/compliance/corpus/swhid-rev/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/swhid-rev/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/swhid-rev/input.json +10 -0
- entviz-0.10.0/compliance/corpus/swhid-rev/model.json +174 -0
- entviz-0.10.0/compliance/corpus/text-hello/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/text-hello/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/text-hello/input.json +10 -0
- entviz-0.10.0/compliance/corpus/text-hello/model.json +108 -0
- entviz-0.10.0/compliance/corpus/text-lorem/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/text-lorem/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/text-lorem/input.json +10 -0
- entviz-0.10.0/compliance/corpus/text-lorem/model.json +334 -0
- entviz-0.10.0/compliance/corpus/ulid-canonical/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ulid-canonical/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ulid-canonical/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ulid-canonical/model.json +174 -0
- entviz-0.10.0/compliance/corpus/ulid-lowercase/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/ulid-lowercase/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/ulid-lowercase/input.json +10 -0
- entviz-0.10.0/compliance/corpus/ulid-lowercase/model.json +174 -0
- entviz-0.10.0/compliance/corpus/uuid-dashed/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/uuid-dashed/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/uuid-dashed/input.json +10 -0
- entviz-0.10.0/compliance/corpus/uuid-dashed/model.json +136 -0
- entviz-0.10.0/compliance/corpus/uuid-max/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/uuid-max/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/uuid-max/input.json +10 -0
- entviz-0.10.0/compliance/corpus/uuid-max/model.json +136 -0
- entviz-0.10.0/compliance/corpus/uuid-nil/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/uuid-nil/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/uuid-nil/input.json +10 -0
- entviz-0.10.0/compliance/corpus/uuid-nil/model.json +136 -0
- entviz-0.10.0/compliance/corpus/uuid-undashed/golden.png +0 -0
- entviz-0.10.0/compliance/corpus/uuid-undashed/golden.svg +1 -0
- entviz-0.10.0/compliance/corpus/uuid-undashed/input.json +10 -0
- entviz-0.10.0/compliance/corpus/uuid-undashed/model.json +136 -0
- entviz-0.10.0/compliance/corpus.py +145 -0
- entviz-0.10.0/compliance/generate.py +103 -0
- entviz-0.10.0/compliance/model.py +381 -0
- entviz-0.10.0/compliance/raster.py +119 -0
- entviz-0.10.0/compliance/runner.py +222 -0
- entviz-0.10.0/docs/assets/cell-layout.svg +1 -0
- entviz-0.10.0/docs/assets/crc.svg +1 -0
- entviz-0.10.0/docs/assets/example.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/01-Random-UUID-v4.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/02-Nil-UUID.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/03-Max-UUID.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/04-Sequential-ends-in-01.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/05-UUID-A.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/06-UUID-A-with-last-char-flipped.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/07-UUID-A-with-mid-char-flipped.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/08-UUID-A-with-first-char-flipped.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/09-64-bit-hex.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/10-128-bit-hex.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/11-256-bit-hex.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/12-512-bit-hex.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/13-6pt.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/14-12pt.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/15-24pt.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/16-Canonical-ULID.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/17-Lowercase-ULID-normalized-up.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/18-ULID-with-I-L-O-aliases.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/19-Ethereum.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/20-Bitcoin-legacy.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/21-Bitcoin-Satoshi-genesis.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/22-Ripple.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/23-Bitcoin-SegWit-P2WPKH.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/24-Bitcoin-SegWit-P2WSH.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/25-Litecoin-ltc1.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/26-Bitcoin-Cash-CashAddr.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/27-Cardano-Shelley.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/28-Cosmos-Hub-checksum-validated-HRP-in-label.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/29-Osmosis-same-generic-bech32-parser.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/30-ssh-ed25519-comment-in-input-dropped-from-entviz.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/31-ssh-rsa-3072-bit-comment-in-input-dropped-from-entviz.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/32-ecdsa-sha2-nistp256-no-comment.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/33-Stellar-account.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/34-Stellar-muxed-account-M.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/35-IPFS-CID-v1.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/36-SWHID-commit-rev.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/37-SWHID-content-cnt.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/38-SWHID-qualified-origin-lines-dropped-free-annotation.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/39-gitoid-blob-sha1-empty-blob.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/40-gitoid-commit-sha1.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/41-gitoid-blob-sha256-empty-blob.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/42-CID-v0-dag-pb-sha2-256.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/43-CID-v1-dag-pb-sha2-256.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/44-CID-v1-raw-sha2-256.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/45-AID-self-addressing-E-Blake3-256.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/46-AID-non-transferable-B-Ed25519.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/47-Verification-key-D-Ed25519.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/48-Ed25519-signature-0B.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/49-Discord-docs-example.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/50-Modern-19-digit-snowflake.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/51-Snowflake-with-neighbor-seq.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/52-Bloomberg-L.P..svg +1 -0
- entviz-0.10.0/docs/assets/gallery/53-Goldman-Sachs-Group-Inc..svg +1 -0
- entviz-0.10.0/docs/assets/gallery/54-JPMorgan-Chase-lowercase-input.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/55-Short-ASCII.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/56-Lorem-ipsum-chunk.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/57-Base64-blob.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/58-RSA-style-key-fragment.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/59-1024-bit-hex.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/60-256-bit-hex-1-1.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/61-256-bit-hex-2-1.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/62-256-bit-hex-1-2.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/63-git-commit-SHA-1-rendered-with---note-git.svg +1 -0
- entviz-0.10.0/docs/assets/gallery/64-git-commit-SHA-256-rendered-with---note-git.svg +1 -0
- entviz-0.10.0/docs/assets/grid-and-cells.svg +1 -0
- entviz-0.10.0/docs/assets/grid-options.svg +1 -0
- entviz-0.10.0/docs/assets/nucleus-channel.svg +1 -0
- entviz-0.10.0/docs/assets/palette-cvd.svg +1 -0
- entviz-0.10.0/docs/assets/palette-swatch.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4a-cell-anatomy.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4a-cell-anatomy.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4b-surround-avalanche.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4b-surround-avalanche.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4c-palette.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4c-palette.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4d-color-bar.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4d-color-bar.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4e-ellipse.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4e-ellipse.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4f-crc.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4f-crc.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-4g-large-input.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-4g-large-input.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-comparison.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-comparison.svg +1 -0
- entviz-0.10.0/docs/assets/paper/fig-hero.png +0 -0
- entviz-0.10.0/docs/assets/paper/fig-hero.svg +1 -0
- entviz-0.10.0/docs/assets/root-commit-entviz.svg +1 -0
- entviz-0.10.0/docs/assets/social-card.png +0 -0
- entviz-0.10.0/docs/assets/social-card.svg +22 -0
- entviz-0.10.0/docs/assets/surround-channel.svg +1 -0
- entviz-0.10.0/docs/assets/text-channel.svg +1 -0
- entviz-0.10.0/docs/assets/tokens.svg +1 -0
- entviz-0.10.0/docs/entviz-paper.md +462 -0
- entviz-0.10.0/docs/gallery.html +540 -0
- entviz-0.10.0/docs/index.md +42 -0
- entviz-0.10.0/docs/spec-change-log.md +70 -0
- entviz-0.10.0/docs/spec-v10-draft.md +152 -0
- entviz-0.10.0/docs/spec.md +587 -0
- entviz-0.10.0/docs/threat-model.md +238 -0
- entviz-0.10.0/experiments/casual-avalanche/FINDINGS.md +137 -0
- entviz-0.10.0/experiments/casual-avalanche/README.md +133 -0
- entviz-0.10.0/experiments/casual-avalanche/ciede2000.py +134 -0
- entviz-0.10.0/experiments/casual-avalanche/diag_blanks.py +67 -0
- entviz-0.10.0/experiments/casual-avalanche/diag_small.py +96 -0
- entviz-0.10.0/experiments/casual-avalanche/levers.py +100 -0
- entviz-0.10.0/experiments/casual-avalanche/metrics.py +71 -0
- entviz-0.10.0/experiments/casual-avalanche/model.py +140 -0
- entviz-0.10.0/experiments/casual-avalanche/paper-section-draft.md +97 -0
- entviz-0.10.0/experiments/casual-avalanche/results/RESULTS.md +75 -0
- entviz-0.10.0/experiments/casual-avalanche/results/raw.json +2249 -0
- entviz-0.10.0/experiments/casual-avalanche/run.py +282 -0
- entviz-0.10.0/gen1.py +127 -0
- entviz-0.10.0/gen1.svg +762 -0
- entviz-0.10.0/gen2.py +188 -0
- entviz-0.10.0/gen2.svg +1214 -0
- entviz-0.10.0/idea-1.svg +122 -0
- entviz-0.10.0/idea-5.svg +80 -0
- entviz-0.10.0/new-shapes/entviz-comparison-matrices.md +76 -0
- entviz-0.10.0/new-shapes/entviz-cubist-final.svg +333 -0
- entviz-0.10.0/new-shapes/entviz-cubist-raster.svg +649 -0
- entviz-0.10.0/new-shapes/entviz-polygons-final.svg +278 -0
- entviz-0.10.0/new-shapes/entviz-polygons-raster.svg +644 -0
- entviz-0.10.0/new-shapes/entviz-svg-encoding.md +173 -0
- entviz-0.10.0/new-shapes/entviz-theory-notes.md +236 -0
- entviz-0.10.0/old-cell.xcf +0 -0
- entviz-0.10.0/overlay-test.html +15 -0
- entviz-0.10.0/prompts/review/architect.md +202 -0
- entviz-0.10.0/prompts/review/devops-engineer.md +240 -0
- entviz-0.10.0/prompts/review/maintainability-expert.md +559 -0
- entviz-0.10.0/prompts/review/orchestrating-reviews.md +263 -0
- entviz-0.10.0/prompts/review/perception-reviewer.md +345 -0
- entviz-0.10.0/prompts/review/security-hawk.md +315 -0
- entviz-0.10.0/prompts/review/spec-conformance-auditor.md +414 -0
- entviz-0.10.0/prompts/review/testability-hawk.md +426 -0
- entviz-0.10.0/proposed-shapes-rotated.svg +101 -0
- entviz-0.10.0/proposed-shapes.svg +56 -0
- entviz-0.10.0/proposed-shapes1.svg +77 -0
- entviz-0.10.0/pyproject.toml +79 -0
- entviz-0.10.0/refs/gallery/all-ones.svg +1 -0
- entviz-0.10.0/refs/gallery/all-zero.svg +1 -0
- entviz-0.10.0/refs/gallery/bitcoin.svg +1 -0
- entviz-0.10.0/refs/gallery/did-key.svg +1 -0
- entviz-0.10.0/refs/gallery/ethereum.svg +1 -0
- entviz-0.10.0/refs/gallery/hex-short.svg +1 -0
- entviz-0.10.0/refs/gallery/index.html +153 -0
- entviz-0.10.0/refs/gallery/ipfs-cidv0.svg +1 -0
- entviz-0.10.0/refs/gallery/rand-a.svg +1 -0
- entviz-0.10.0/refs/gallery/rand-b.svg +1 -0
- entviz-0.10.0/refs/gallery/sha256-empty.svg +1 -0
- entviz-0.10.0/refs/gallery/sha512-large.svg +1 -0
- entviz-0.10.0/refs/gallery/uuid-a.svg +1 -0
- entviz-0.10.0/refs/gallery/uuid-b.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/all-ones.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/all-zeros.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/bitcoin.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/did-key.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/ethereum.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/index.html +184 -0
- entviz-0.10.0/refs/gallery-v3/ipfs-cidv0.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/rand-128-a.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/rand-128-b.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/random-256.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha256-abc.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha256-empty.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha384.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha512-large-a.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha512-large-b.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/sha512.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/uuid-a.svg +1 -0
- entviz-0.10.0/refs/gallery-v3/uuid-b.svg +1 -0
- entviz-0.10.0/refs/gallery3/all-ones.svg +1 -0
- entviz-0.10.0/refs/gallery3/all-zero.svg +1 -0
- entviz-0.10.0/refs/gallery3/bitcoin.svg +1 -0
- entviz-0.10.0/refs/gallery3/did-key.svg +1 -0
- entviz-0.10.0/refs/gallery3/ethereum.svg +1 -0
- entviz-0.10.0/refs/gallery3/hex-short.svg +1 -0
- entviz-0.10.0/refs/gallery3/index.html +128 -0
- entviz-0.10.0/refs/gallery3/ipfs-cidv0.svg +1 -0
- entviz-0.10.0/refs/gallery3/rand-a.svg +1 -0
- entviz-0.10.0/refs/gallery3/rand-b.svg +1 -0
- entviz-0.10.0/refs/gallery3/sha256-empty.svg +1 -0
- entviz-0.10.0/refs/gallery3/sha512-large.svg +1 -0
- entviz-0.10.0/refs/gallery3/uuid-a.svg +1 -0
- entviz-0.10.0/refs/gallery3/uuid-b.svg +1 -0
- entviz-0.10.0/refs/gallery4/all-ones.svg +1 -0
- entviz-0.10.0/refs/gallery4/all-zero.svg +1 -0
- entviz-0.10.0/refs/gallery4/bitcoin.svg +1 -0
- entviz-0.10.0/refs/gallery4/did-key.svg +1 -0
- entviz-0.10.0/refs/gallery4/ethereum.svg +1 -0
- entviz-0.10.0/refs/gallery4/hex-short.svg +1 -0
- entviz-0.10.0/refs/gallery4/index.html +38 -0
- entviz-0.10.0/refs/gallery4/ipfs-cidv0.svg +1 -0
- entviz-0.10.0/refs/gallery4/rand-a.svg +1 -0
- entviz-0.10.0/refs/gallery4/rand-b.svg +1 -0
- entviz-0.10.0/refs/gallery4/sha256-empty.svg +1 -0
- entviz-0.10.0/refs/gallery4/sha512-large.svg +1 -0
- entviz-0.10.0/refs/gallery4/uuid-a.svg +1 -0
- entviz-0.10.0/refs/gallery4/uuid-b.svg +1 -0
- entviz-0.10.0/refs/inputs.txt +17 -0
- entviz-0.10.0/refs/overlay-options/01-3x3-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/02-3x3-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/03-3x3-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/04-3x4-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/05-3x4-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/06-3x4-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/07-4x4-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/08-4x4-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/09-4x4-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/10-4x6-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/11-4x6-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/12-4x6-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/13-5x6-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/14-5x6-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/15-5x6-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/16-6x5-grid-1.svg +1 -0
- entviz-0.10.0/refs/overlay-options/17-6x5-grid-2.svg +1 -0
- entviz-0.10.0/refs/overlay-options/18-6x5-grid-3.svg +1 -0
- entviz-0.10.0/refs/overlay-options/index.html +148 -0
- entviz-0.10.0/refs/phase10/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase10/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase10/large-576.svg +1 -0
- entviz-0.10.0/refs/phase10/short.svg +1 -0
- entviz-0.10.0/refs/phase10/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase10/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase11/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase11/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase11/large-576.svg +1 -0
- entviz-0.10.0/refs/phase11/short.svg +1 -0
- entviz-0.10.0/refs/phase11/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase11/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase12/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase12/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase12/large-576.svg +1 -0
- entviz-0.10.0/refs/phase12/short.svg +1 -0
- entviz-0.10.0/refs/phase12/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase12/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase4/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase4/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase4/large-576.svg +1 -0
- entviz-0.10.0/refs/phase4/short.svg +1 -0
- entviz-0.10.0/refs/phase4/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase4/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase5/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase5/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase5/large-576.svg +1 -0
- entviz-0.10.0/refs/phase5/short.svg +1 -0
- entviz-0.10.0/refs/phase5/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase5/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase7/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase7/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase7/large-576.svg +1 -0
- entviz-0.10.0/refs/phase7/short.svg +1 -0
- entviz-0.10.0/refs/phase7/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase7/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase8/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase8/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase8/large-576.svg +1 -0
- entviz-0.10.0/refs/phase8/short.svg +1 -0
- entviz-0.10.0/refs/phase8/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase8/uuid-b.svg +1 -0
- entviz-0.10.0/refs/phase9/all-aaa.svg +1 -0
- entviz-0.10.0/refs/phase9/exact-512.svg +1 -0
- entviz-0.10.0/refs/phase9/large-576.svg +1 -0
- entviz-0.10.0/refs/phase9/short.svg +1 -0
- entviz-0.10.0/refs/phase9/uuid-a.svg +1 -0
- entviz-0.10.0/refs/phase9/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v1/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v1/exact-512.svg +1 -0
- entviz-0.10.0/refs/v1/large-576.svg +1 -0
- entviz-0.10.0/refs/v1/short.svg +1 -0
- entviz-0.10.0/refs/v1/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v1/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v2/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v2/exact-512.svg +1 -0
- entviz-0.10.0/refs/v2/large-576.svg +1 -0
- entviz-0.10.0/refs/v2/short.svg +1 -0
- entviz-0.10.0/refs/v2/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v2/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v3-1/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v3-1/exact-512.svg +1 -0
- entviz-0.10.0/refs/v3-1/large-576.svg +1 -0
- entviz-0.10.0/refs/v3-1/short.svg +1 -0
- entviz-0.10.0/refs/v3-1/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v3-1/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v3-5/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v3-5/exact-512.svg +1 -0
- entviz-0.10.0/refs/v3-5/large-576.svg +1 -0
- entviz-0.10.0/refs/v3-5/short.svg +1 -0
- entviz-0.10.0/refs/v3-5/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v3-5/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v3-6b/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v3-6b/exact-512.svg +1 -0
- entviz-0.10.0/refs/v3-6b/large-576.svg +1 -0
- entviz-0.10.0/refs/v3-6b/short.svg +1 -0
- entviz-0.10.0/refs/v3-6b/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v3-6b/uuid-b.svg +1 -0
- entviz-0.10.0/refs/v3-7/all-aaa.svg +1 -0
- entviz-0.10.0/refs/v3-7/exact-512.svg +1 -0
- entviz-0.10.0/refs/v3-7/large-576.svg +1 -0
- entviz-0.10.0/refs/v3-7/short.svg +1 -0
- entviz-0.10.0/refs/v3-7/uuid-a.svg +1 -0
- entviz-0.10.0/refs/v3-7/uuid-b.svg +1 -0
- entviz-0.10.0/reviews/adversarial-2026-05-27.md +317 -0
- entviz-0.10.0/reviews/adversarial-2026-06-02.md +481 -0
- entviz-0.10.0/reviews/devops-engineer-2026-06-05 review.md +186 -0
- entviz-0.10.0/reviews/devops-engineer-2026-06-08 review.md +192 -0
- entviz-0.10.0/reviews/ellipse-audit-2026-06-02.md +190 -0
- entviz-0.10.0/reviews/maintainability-expert-2026-06-05 review.md +247 -0
- entviz-0.10.0/reviews/maintainability-expert-2026-06-08 review.md +246 -0
- entviz-0.10.0/reviews/palette-optimization-findings.md +182 -0
- entviz-0.10.0/reviews/paper-todo.md +84 -0
- entviz-0.10.0/reviews/paper-v6-update-plan.md +222 -0
- entviz-0.10.0/reviews/perception-reviewer-2026-06-08 review.md +267 -0
- entviz-0.10.0/reviews/review-panel-2026-06-05 review.md +53 -0
- entviz-0.10.0/reviews/review-panel-2026-06-08 review.md +68 -0
- entviz-0.10.0/reviews/security-hawk-2026-06-05 review.md +190 -0
- entviz-0.10.0/reviews/security-hawk-2026-06-08 review.md +211 -0
- entviz-0.10.0/reviews/spec-conformance-auditor-2026-06-08 review.md +208 -0
- entviz-0.10.0/reviews/spec-improvement-notes.md +716 -0
- entviz-0.10.0/reviews/testability-hawk-2026-06-05 review.md +209 -0
- entviz-0.10.0/reviews/testability-hawk-2026-06-08 review.md +194 -0
- entviz-0.10.0/sample-entvizes-gradients.svg +1330 -0
- entviz-0.10.0/sample-entvizes.svg +830 -0
- entviz-0.10.0/scripts/check_unicode.py +103 -0
- entviz-0.10.0/scripts/ellipse_prototype.py +172 -0
- entviz-0.10.0/scripts/figlib.py +419 -0
- entviz-0.10.0/scripts/gallery.py +363 -0
- entviz-0.10.0/scripts/paper_figures.py +505 -0
- entviz-0.10.0/scripts/release.py +247 -0
- entviz-0.10.0/scripts/social_card.py +268 -0
- entviz-0.10.0/scripts/spec_figures.py +414 -0
- entviz-0.10.0/src/entviz/__init__.py +23 -0
- entviz-0.10.0/src/entviz/app.py +53 -0
- entviz-0.10.0/src/entviz/colors.py +121 -0
- entviz-0.10.0/src/entviz/entropy.py +1494 -0
- entviz-0.10.0/src/entviz/fingerprint.py +70 -0
- entviz-0.10.0/src/entviz/keccak.py +130 -0
- entviz-0.10.0/src/entviz/layout.py +205 -0
- entviz-0.10.0/src/entviz/pipeline.py +1170 -0
- entviz-0.10.0/src/entviz/renderer.py +137 -0
- entviz-0.10.0/src/entviz/shapes.py +23 -0
- entviz-0.10.0/tests/test_adversarial_additional.py +305 -0
- entviz-0.10.0/tests/test_adversarial_fixes.py +204 -0
- entviz-0.10.0/tests/test_app.py +141 -0
- entviz-0.10.0/tests/test_cesr_semantic_prefix.py +153 -0
- entviz-0.10.0/tests/test_check_unicode.py +137 -0
- entviz-0.10.0/tests/test_cid_multicodec.py +80 -0
- entviz-0.10.0/tests/test_colors.py +31 -0
- entviz-0.10.0/tests/test_compliance.py +89 -0
- entviz-0.10.0/tests/test_crc_tokens.py +52 -0
- entviz-0.10.0/tests/test_entropy.py +234 -0
- entviz-0.10.0/tests/test_extra_alphabets.py +66 -0
- entviz-0.10.0/tests/test_f7_eip55.py +218 -0
- entviz-0.10.0/tests/test_figure_marker_colors.py +47 -0
- entviz-0.10.0/tests/test_figures.py +92 -0
- entviz-0.10.0/tests/test_fingerprint.py +99 -0
- entviz-0.10.0/tests/test_gallery.py +95 -0
- entviz-0.10.0/tests/test_grid_selection.py +108 -0
- entviz-0.10.0/tests/test_large_input.py +138 -0
- entviz-0.10.0/tests/test_layout.py +75 -0
- entviz-0.10.0/tests/test_phase10_color_bar.py +77 -0
- entviz-0.10.0/tests/test_phase12_ellipse.py +129 -0
- entviz-0.10.0/tests/test_phase13_validation.py +189 -0
- entviz-0.10.0/tests/test_phase4_avalanche.py +100 -0
- entviz-0.10.0/tests/test_phase7_geometry.py +81 -0
- entviz-0.10.0/tests/test_phase8_layering.py +116 -0
- entviz-0.10.0/tests/test_pipeline.py +135 -0
- entviz-0.10.0/tests/test_shapes.py +30 -0
- entviz-0.10.0/tests/test_snowflake.py +232 -0
- entviz-0.10.0/tests/test_social_card.py +101 -0
- entviz-0.10.0/tests/test_swhid_gitoid.py +166 -0
- entviz-0.10.0/tests/test_tokenization.py +60 -0
- entviz-0.10.0/tests/test_user_note.py +91 -0
- entviz-0.10.0/tests/test_v10_casual_avalanche.py +196 -0
- entviz-0.10.0/tests/test_v3_color_bar_skew.py +62 -0
- entviz-0.10.0/tests/test_v3_ellipse.py +175 -0
- entviz-0.10.0/tests/test_v3_hex_font.py +89 -0
- entviz-0.10.0/tests/test_v4_base32.py +126 -0
- entviz-0.10.0/tests/test_v4_bech32.py +151 -0
- entviz-0.10.0/tests/test_v4_clip_id_uniqueness.py +63 -0
- entviz-0.10.0/tests/test_v4_color_bar_source.py +60 -0
- entviz-0.10.0/tests/test_v4_contrast_threshold.py +98 -0
- entviz-0.10.0/tests/test_v4_disproof_fallback.py +110 -0
- entviz-0.10.0/tests/test_v4_ethereum_detection.py +104 -0
- entviz-0.10.0/tests/test_v4_hex_lowercase.py +83 -0
- entviz-0.10.0/tests/test_v4_label_regions.py +229 -0
- entviz-0.10.0/tests/test_v4_lei.py +178 -0
- entviz-0.10.0/tests/test_v4_overlay_opacity.py +75 -0
- entviz-0.10.0/tests/test_v4_quartile_marks.py +77 -0
- entviz-0.10.0/tests/test_v4_small_grid_ellipse.py +130 -0
- entviz-0.10.0/tests/test_v4_ssh_keys.py +239 -0
- entviz-0.10.0/tests/test_v4_surround.py +123 -0
- entviz-0.10.0/tests/test_v4_taller_nucleus.py +84 -0
- entviz-0.10.0/tests/test_v4_ulid.py +178 -0
- entviz-0.10.0/tests/test_v5_color_bar_letters.py +196 -0
- entviz-0.10.0/tests/test_v5_data_attributes.py +369 -0
- entviz-0.10.0/tests/test_v5_ellipse_rmin.py +92 -0
- entviz-0.10.0/tests/test_v5_truncation.py +115 -0
- entviz-0.10.0/tests/test_v6_blank_map.py +252 -0
- entviz-0.10.0/tests/test_v6_fingerprint_middle.py +267 -0
- entviz-0.10.0/tests/test_v6_palette_lightness.py +130 -0
- entviz-0.10.0/tests/test_v9_color_bar.py +104 -0
- entviz-0.10.0/this.i +3254 -0
- entviz-0.10.0/uv.lock +950 -0
- entviz-0.10.0/x.svg +1 -0
- entviz-0.10.0/zensical.toml +66 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-board
|
|
3
|
+
description: >-
|
|
4
|
+
Run entviz's multi-persona adversarial review panel over this repo. Spawns a
|
|
5
|
+
vendored Workflow that fans out specialized reviewers — spec-conformance &
|
|
6
|
+
determinism (correctness/cryptographic rigor), perception/psychophysics
|
|
7
|
+
(human discriminability/CVD/grayscale), testability, maintainability,
|
|
8
|
+
security, and devops (architect on request) — adversarially refutes
|
|
9
|
+
high-stakes findings, dedupes by concept, and writes per-persona + synthesis
|
|
10
|
+
reports to reviews/. Use when the user says "run the review board", "run a
|
|
11
|
+
review panel on entviz", "review the code with the personas", or wants a
|
|
12
|
+
broad multi-lens code review at a milestone. Read-only on source; writes only
|
|
13
|
+
to reviews/ (uncommitted). Opt-in / explicit invocation only — it can spawn
|
|
14
|
+
many subagents.
|
|
15
|
+
user-invocable: true
|
|
16
|
+
allowed-tools: Bash, Read, Workflow
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# /review-board — entviz multi-persona adversarial review panel
|
|
20
|
+
|
|
21
|
+
Runs entviz's own vendored review panel: a deterministic `Workflow` that spawns
|
|
22
|
+
one specialized reviewer per persona, verifies the high-stakes findings, dedupes
|
|
23
|
+
across personas, and persists the reports to `reviews/`.
|
|
24
|
+
|
|
25
|
+
This is **entviz-specific tooling** — self-contained in this repo, with no
|
|
26
|
+
dependency on any external prompt clone. The persona prompts live in
|
|
27
|
+
[`prompts/review/`](../../../prompts/review/) and the workflow script in
|
|
28
|
+
[`.claude/workflows/review-panel.js`](../../workflows/review-panel.js). The
|
|
29
|
+
panel reads source **read-only** and writes its output to `reviews/`
|
|
30
|
+
(uncommitted); it never commits, and never touches `this.i`.
|
|
31
|
+
|
|
32
|
+
## When to run
|
|
33
|
+
|
|
34
|
+
At a milestone, before a release, or after a substantial change to the renderer,
|
|
35
|
+
the spec, the fingerprint/color/layout code, or the conformance suite. It is
|
|
36
|
+
**opt-in**: only run it when the user explicitly asks for it (it can spawn a
|
|
37
|
+
dozen-plus subagents and is token-heavy).
|
|
38
|
+
|
|
39
|
+
## How to run
|
|
40
|
+
|
|
41
|
+
1. **Resolve the target.** This skill always reviews **this repo**. Capture the
|
|
42
|
+
repo root and today's date from a trustworthy shell (the session cwd), so the
|
|
43
|
+
workflow targets the right tree and stamps the milestone:
|
|
44
|
+
```bash
|
|
45
|
+
git rev-parse --show-toplevel
|
|
46
|
+
git rev-parse --abbrev-ref HEAD
|
|
47
|
+
date +%F
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
2. **Pick the persona set.** Default panel (omit `personas`): `SPEC, PSY, TST,
|
|
51
|
+
MNT, SEC, OPS`. Add the architect lens by passing
|
|
52
|
+
`personas: ['SPEC','PSY','TST','MNT','SEC','OPS','ARC']`, or pass
|
|
53
|
+
`personas: 'auto'` to let a git-aware Scope phase choose lenses from the diff
|
|
54
|
+
and skip ones a recent review already covers. The user may also name a
|
|
55
|
+
subset (e.g. "just spec-conformance and perception" →
|
|
56
|
+
`personas: ['SPEC','PSY']`).
|
|
57
|
+
|
|
58
|
+
3. **Invoke the Workflow** with the resolved absolute repo root as `target` and
|
|
59
|
+
a dated milestone label. Example:
|
|
60
|
+
```
|
|
61
|
+
Workflow({
|
|
62
|
+
scriptPath: '<repo-root>/.claude/workflows/review-panel.js',
|
|
63
|
+
args: {
|
|
64
|
+
target: '<repo-root>', // absolute path from step 1
|
|
65
|
+
milestone: '<YYYY-MM-DD> review', // from `date +%F`
|
|
66
|
+
// personas: ['SPEC','PSY','TST','MNT','SEC','OPS'], // omit for the default six
|
|
67
|
+
// verify: 'default', // 'off' | 'default' | 'all' — adversarial refutation of high-stakes findings
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
The Workflow returns immediately with a task id and notifies you on
|
|
72
|
+
completion; watch live progress with `/workflows`.
|
|
73
|
+
|
|
74
|
+
4. **Relay the result.** When it completes, read the synthesis report it wrote
|
|
75
|
+
(`reviews/review-panel-<milestone>.md`) and summarize for the user: the
|
|
76
|
+
posture, the CRITICAL/HIGH `recommend-fix` blockers, anything the verify pass
|
|
77
|
+
refuted, and the per-persona report filenames. The reports are uncommitted —
|
|
78
|
+
leave committing to the user.
|
|
79
|
+
|
|
80
|
+
## Knobs (all optional)
|
|
81
|
+
|
|
82
|
+
| Arg | Default | Meaning |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `target` | — (required) | Absolute repo root (or relative + `baseDir`). |
|
|
85
|
+
| `milestone` | `"review"` | Run label; goes in every report filename. |
|
|
86
|
+
| `personas` | the default six | Array of prefixes/names, or `'auto'` for git-aware scoping. |
|
|
87
|
+
| `verify` | `'default'` | `'off'` / `'default'` (spec-conformance, security, or any CRITICAL) / `'all'` (every CRITICAL+HIGH recommend-fix). |
|
|
88
|
+
| `effort` | per-persona | Run-wide override (`'medium'`/`'deep'`). |
|
|
89
|
+
| `model` | per-persona | Run-wide model override. |
|
|
90
|
+
| `overrides` | — | Per-persona `{PREFIX: {effort, model}}`. |
|
|
91
|
+
| `concurrency` | `3` | Personas fan out in chunks of this many (RAM ceiling). |
|
|
92
|
+
|
|
93
|
+
## Personas
|
|
94
|
+
|
|
95
|
+
| Prefix | Lens | Default |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `SPEC` | Spec-conformance & determinism — provable correctness, cryptographic rigor, cross-impl parity | deep / strongest model |
|
|
98
|
+
| `PSY` | Perception & psychophysics — discriminability, CVD, grayscale, JND, oral readout | deep |
|
|
99
|
+
| `SEC` | Implementation security — SVG/HTML injection, path traversal, supply-chain, parser DoS | medium |
|
|
100
|
+
| `OPS` | DevOps — CI/CD, release/publish gating, Pages deploy, action pinning | medium |
|
|
101
|
+
| `ARC` | Architecture — module decomposition, the spec↔impl boundary, multi-impl readiness (opt-in) | deep |
|
|
102
|
+
| `TST` | Testability — TDD discipline, conformance-suite coverage, determinism tests | medium |
|
|
103
|
+
| `MNT` | Maintainability — intent boundaries (`this.i`), naming, idiom, dead code, stale docs | medium |
|
|
104
|
+
|
|
105
|
+
See [`prompts/review/orchestrating-reviews.md`](../../../prompts/review/orchestrating-reviews.md)
|
|
106
|
+
for the severity scale, the `dedupe_key` convention, and the manifest schema the
|
|
107
|
+
personas share.
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'entviz-review-panel',
|
|
3
|
+
description: 'Multi-persona adversarial code review of the entviz repo; dedupes findings by dedupe_key and adjudicates dispositions. Self-contained — persona prompts are vendored under prompts/review/. args is an OBJECT (see whenToUse).',
|
|
4
|
+
whenToUse: 'At a milestone, for a multi-lens review of entviz. Reads source read-only but WRITES its output to <target>/reviews/ (uncommitted): one synthesis file plus a per-persona report each, then returns the triaged queue + summary. ARGS (object): target = the entviz repo folder, relative ("." / "../entviz") or absolute; for a relative target also pass baseDir = the launching session absolute cwd. For "this repo"/no explicit target, the orchestrator resolves the session git root (git rev-parse --show-toplevel) and passes it as target. personas = optional array of names ("spec","perception","security","devops","architect","testability","maintainability") or prefixes ("SPEC","PSY","SEC","OPS","ARC","TST","MNT"); omit to run the DEFAULT six (spec, perception, testability, maintainability, security, devops) and name "architect" to add it; or the string "auto" to let a git-aware Scope phase pick lenses from the diff/history. Optional: branch (abort if target is not on it), milestone (run label), concurrency (default 3), promptsDir (override the vendored prompts/review/ location). Effort/model tiering: spec-conformance runs deep+Opus, perception+architect deep+Sonnet, the rest medium+Sonnet; override run-wide with effort + model, or per-persona with overrides = {PREFIX: {effort, model}}. verify ("off"|"default"|"all", default "default") runs a pre-merge pass that adversarially tries to REFUTE high-stakes findings (default = spec-conformance/security/CRITICAL; "all" = every CRITICAL+HIGH recommend-fix); refuted findings are excluded from the queue but recorded in the report.',
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Preflight', detail: 'verify the target repo + branch exist (and stamp the reviewed commit) before spending on personas' },
|
|
7
|
+
{ title: 'Scope', detail: 'git-aware lens selection (only when personas: "auto"): map changes to lenses, skip lenses a recent review already covers' },
|
|
8
|
+
{ title: 'Review', detail: 'one agent per persona, unattended, chunked to respect the RAM ceiling' },
|
|
9
|
+
{ title: 'Verify', detail: 'adversarially refute high-stakes findings (spec-conformance/security/CRITICAL) before merge; refuted findings are excluded but recorded' },
|
|
10
|
+
{ title: 'Synthesize', detail: 'merge by dedupe_key (most-obligated severity wins) + executive summary' },
|
|
11
|
+
{ title: 'Persist', detail: 'write the synthesis + per-persona reports to <target>/reviews/' },
|
|
12
|
+
],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ---- inputs ----
|
|
16
|
+
// args.target may be relative ("." / "../entviz") or absolute. A relative target is only unambiguous
|
|
17
|
+
// against an explicit base, so it requires args.baseDir = the launching session's absolute cwd.
|
|
18
|
+
// We never resolve a relative path against the workflow agents' ambient cwd. The preflight
|
|
19
|
+
// canonicalizes whatever we get to the enclosing git repo root.
|
|
20
|
+
if (!args || typeof args.target !== 'string' || !args.target.trim()) {
|
|
21
|
+
throw new Error('review-panel requires args.target = the entviz repo folder (relative like "." / "../entviz", or absolute). For a relative target, also pass args.baseDir = the absolute directory it is relative to.')
|
|
22
|
+
}
|
|
23
|
+
const TARGET_INPUT = args.target.trim()
|
|
24
|
+
const BASE_DIR = args && typeof args.baseDir === 'string' ? args.baseDir.replace(/\/+$/, '') : null
|
|
25
|
+
const targetIsAbsolute = TARGET_INPUT.startsWith('/')
|
|
26
|
+
if (!targetIsAbsolute && !(BASE_DIR && BASE_DIR.startsWith('/'))) {
|
|
27
|
+
throw new Error(`review-panel got a relative target "${TARGET_INPUT}" but no absolute args.baseDir to resolve it against. The orchestrator must pass baseDir = the launching repo's absolute path (the session cwd); a relative path must never resolve against the workflow agents' ambient cwd.`)
|
|
28
|
+
}
|
|
29
|
+
// Directory the preflight inspects; git resolves any ".." / subdir to the canonical repo root.
|
|
30
|
+
const CANDIDATE = targetIsAbsolute ? TARGET_INPUT.replace(/\/+$/, '') : `${BASE_DIR}/${TARGET_INPUT}`
|
|
31
|
+
const BRANCH = (args && args.branch) || null // optional; if set, the run aborts on mismatch
|
|
32
|
+
const milestone = (args && args.milestone) || 'review'
|
|
33
|
+
const CONCURRENCY = (args && args.concurrency) || 3 // self-throttle for this RAM-limited machine
|
|
34
|
+
|
|
35
|
+
// Per-persona effort/model defaults. entviz's headline claims are CORRECTNESS and PERCEPTION, so
|
|
36
|
+
// spec-conformance runs deep on the strongest model (its findings can invalidate every previously
|
|
37
|
+
// rendered entviz), perception and architect run deep on Sonnet, and the inherited defect lenses run
|
|
38
|
+
// cheap (medium effort, Sonnet). All OVERRIDABLE via args.effort / args.model / args.overrides.
|
|
39
|
+
const ALL_PERSONAS = [
|
|
40
|
+
{ file: 'spec-conformance-auditor.md', prefix: 'SPEC', persona: 'spec-conformance-auditor', effort: 'deep', model: 'claude-opus-4-8' },
|
|
41
|
+
{ file: 'perception-reviewer.md', prefix: 'PSY', persona: 'perception-reviewer', effort: 'deep', model: 'claude-sonnet-4-6' },
|
|
42
|
+
{ file: 'security-hawk.md', prefix: 'SEC', persona: 'security-hawk', effort: 'medium', model: 'claude-sonnet-4-6' },
|
|
43
|
+
{ file: 'devops-engineer.md', prefix: 'OPS', persona: 'devops-engineer', effort: 'medium', model: 'claude-sonnet-4-6' },
|
|
44
|
+
{ file: 'architect.md', prefix: 'ARC', persona: 'architect', effort: 'deep', model: 'claude-sonnet-4-6' },
|
|
45
|
+
{ file: 'testability-hawk.md', prefix: 'TST', persona: 'testability-hawk', effort: 'medium', model: 'claude-sonnet-4-6' },
|
|
46
|
+
{ file: 'maintainability-expert.md', prefix: 'MNT', persona: 'maintainability-expert', effort: 'medium', model: 'claude-sonnet-4-6' },
|
|
47
|
+
]
|
|
48
|
+
// Accept either prefixes (SPEC, PSY, …) or friendly names.
|
|
49
|
+
const NAME_TO_PREFIX = {
|
|
50
|
+
spec: 'SPEC', conformance: 'SPEC', 'spec-conformance': 'SPEC', 'spec-conformance-auditor': 'SPEC', correctness: 'SPEC',
|
|
51
|
+
psy: 'PSY', perception: 'PSY', psychophysics: 'PSY', 'perception-reviewer': 'PSY', ux: 'PSY',
|
|
52
|
+
sec: 'SEC', security: 'SEC', 'security-hawk': 'SEC',
|
|
53
|
+
ops: 'OPS', devops: 'OPS', 'devops-engineer': 'OPS',
|
|
54
|
+
arc: 'ARC', architect: 'ARC', architecture: 'ARC',
|
|
55
|
+
tst: 'TST', test: 'TST', testability: 'TST', 'testability-hawk': 'TST',
|
|
56
|
+
mnt: 'MNT', maint: 'MNT', maintainability: 'MNT', 'maintainability-expert': 'MNT',
|
|
57
|
+
}
|
|
58
|
+
const toPrefix = (t) => NAME_TO_PREFIX[String(t).toLowerCase().trim()] || String(t).toUpperCase().trim()
|
|
59
|
+
const VALID_PREFIXES = new Set(ALL_PERSONAS.map((p) => p.prefix))
|
|
60
|
+
// Default panel when no personas are named: spec, perception, testability, maintainability, security, devops.
|
|
61
|
+
// Name architect explicitly via args.personas to add it.
|
|
62
|
+
const DEFAULT_PREFIXES = ['SPEC', 'PSY', 'TST', 'MNT', 'SEC', 'OPS']
|
|
63
|
+
// args.personas may be: an array of names/prefixes (mapped now); the string 'auto' (resolved later
|
|
64
|
+
// by the git-aware Scope phase, after preflight); or omitted (DEFAULT_PREFIXES).
|
|
65
|
+
const AUTO_SCOPE = args && args.personas === 'auto'
|
|
66
|
+
let wanted = AUTO_SCOPE ? null : (args && Array.isArray(args.personas) ? args.personas.map(toPrefix) : DEFAULT_PREFIXES)
|
|
67
|
+
let PERSONAS = AUTO_SCOPE ? null : ALL_PERSONAS.filter((p) => wanted.includes(p.prefix))
|
|
68
|
+
|
|
69
|
+
const FINDINGS_SCHEMA = {
|
|
70
|
+
type: 'object',
|
|
71
|
+
required: ['findings'],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
properties: {
|
|
74
|
+
findings: {
|
|
75
|
+
type: 'array',
|
|
76
|
+
items: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
additionalProperties: false,
|
|
79
|
+
required: ['id', 'persona', 'title', 'severity', 'confidence', 'location', 'dedupe_key', 'recommended_disposition', 'rationale', 'fix_effort'],
|
|
80
|
+
properties: {
|
|
81
|
+
id: { type: 'string' },
|
|
82
|
+
persona: { type: 'string' },
|
|
83
|
+
title: { type: 'string' },
|
|
84
|
+
severity: { enum: ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'] },
|
|
85
|
+
confidence: { enum: ['CONFIRMED', 'LIKELY', 'SPECULATIVE'] },
|
|
86
|
+
location: { type: 'string' },
|
|
87
|
+
dedupe_key: { type: 'string' },
|
|
88
|
+
recommended_disposition: { enum: ['recommend-fix', 'recommend-defer', 'recommend-accept-risk'] },
|
|
89
|
+
rationale: { type: 'string' },
|
|
90
|
+
revisit_condition: { type: ['string', 'null'] },
|
|
91
|
+
fix_effort: { enum: ['small', 'medium', 'large'] },
|
|
92
|
+
tier: { type: ['integer', 'null'] },
|
|
93
|
+
cost_category: { type: ['string', 'null'] },
|
|
94
|
+
measurement: { type: ['string', 'null'] },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function runChunked(items, size, fn) {
|
|
102
|
+
const out = []
|
|
103
|
+
for (let i = 0; i < items.length; i += size) {
|
|
104
|
+
const results = await parallel(items.slice(i, i + size).map((it) => () => fn(it)))
|
|
105
|
+
out.push(...results)
|
|
106
|
+
}
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ---- Phase 0: preflight — confirm we are about to review the RIGHT repo+branch ----
|
|
111
|
+
phase('Preflight')
|
|
112
|
+
const PREFLIGHT_SCHEMA = {
|
|
113
|
+
type: 'object',
|
|
114
|
+
required: ['exists', 'toplevel', 'branch', 'head_sha', 'dirty'],
|
|
115
|
+
additionalProperties: false,
|
|
116
|
+
properties: {
|
|
117
|
+
exists: { type: 'boolean' },
|
|
118
|
+
toplevel: { type: ['string', 'null'] },
|
|
119
|
+
branch: { type: ['string', 'null'] },
|
|
120
|
+
head_sha: { type: ['string', 'null'] },
|
|
121
|
+
dirty: { type: 'boolean' },
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
const pf = await agent(
|
|
125
|
+
`Run exactly these against "${CANDIDATE}" and report the results — make NO edits, review nothing yet:\n` +
|
|
126
|
+
` git -C "${CANDIDATE}" rev-parse --show-toplevel\n` +
|
|
127
|
+
` git -C "${CANDIDATE}" rev-parse --abbrev-ref HEAD\n` +
|
|
128
|
+
` git -C "${CANDIDATE}" rev-parse HEAD\n` +
|
|
129
|
+
` git -C "${CANDIDATE}" status --porcelain\n` +
|
|
130
|
+
`Return {exists: true/false (false if "${CANDIDATE}" does not exist or is not inside a git repo), toplevel: <absolute repo root, or null>, branch: <name or null>, head_sha: <full HEAD sha or null>, dirty: true/false (true if the status --porcelain output is non-empty)}.`,
|
|
131
|
+
{ label: 'preflight', phase: 'Preflight', schema: PREFLIGHT_SCHEMA },
|
|
132
|
+
)
|
|
133
|
+
if (!pf || !pf.exists || !pf.toplevel)
|
|
134
|
+
return { error: `target does not resolve to a git repo: "${TARGET_INPUT}"${targetIsAbsolute ? '' : ` (relative to ${BASE_DIR})`} → ${CANDIDATE}`, preflight: pf }
|
|
135
|
+
// Canonical absolute repo root — a subdir or relative input auto-corrects to here. Used everywhere downstream.
|
|
136
|
+
const TARGET = pf.toplevel.replace(/\/+$/, '')
|
|
137
|
+
if (BRANCH && pf.branch !== BRANCH)
|
|
138
|
+
return { error: `branch mismatch: wanted "${BRANCH}", ${TARGET} is on "${pf.branch}". Aborting rather than reviewing the wrong branch.`, preflight: pf }
|
|
139
|
+
const reviewedBranch = pf.branch
|
|
140
|
+
const reviewedSha = pf.head_sha
|
|
141
|
+
const dirty = pf.dirty
|
|
142
|
+
log(`Resolved "${TARGET_INPUT}" → ${TARGET} (branch ${reviewedBranch}, commit ${reviewedSha || 'unknown'}${dirty ? ', dirty working tree' : ''})`)
|
|
143
|
+
const reviewsDir = ((args && args.reviewsDir) || `${TARGET}/reviews`).replace(/\/+$/, '')
|
|
144
|
+
// Persona prompts are VENDORED in the entviz repo (self-contained — no external clone dependency).
|
|
145
|
+
// Default to <repo>/prompts/review/; overridable via args.promptsDir.
|
|
146
|
+
const PROMPTS_DIR = ((args && args.promptsDir) || `${TARGET}/prompts/review`).replace(/\/+$/, '')
|
|
147
|
+
|
|
148
|
+
// ---- Phase 0.5: git-aware scoping (only when args.personas === 'auto') ----
|
|
149
|
+
if (AUTO_SCOPE) {
|
|
150
|
+
phase('Scope')
|
|
151
|
+
const SCOPE_SCHEMA = {
|
|
152
|
+
type: 'object',
|
|
153
|
+
required: ['personas', 'skipped'],
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
properties: {
|
|
156
|
+
personas: { type: 'array', items: { type: 'string' } },
|
|
157
|
+
skipped: {
|
|
158
|
+
type: 'array',
|
|
159
|
+
items: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
additionalProperties: false,
|
|
162
|
+
required: ['prefix', 'reason'],
|
|
163
|
+
properties: { prefix: { type: 'string' }, reason: { type: 'string' } },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
const personaCatalog = ALL_PERSONAS.map((p) => `${p.prefix} (${p.file.replace(/\.md$/, '')})`).join(', ')
|
|
169
|
+
const scope = await agent(
|
|
170
|
+
`TARGETING — read carefully: before anything, run \`git -C "${TARGET}" rev-parse --show-toplevel\` and confirm ` +
|
|
171
|
+
`it equals "${TARGET}". If it does NOT, STOP and return {"personas":[],"skipped":[]}. Inspect ONLY "${TARGET}".\n\n` +
|
|
172
|
+
`You are scoping which code-review lenses to run for this milestone of entviz. Inspect, under \`nice -n 19 ionice -c 3\`:\n` +
|
|
173
|
+
` git -C "${TARGET}" status --porcelain\n` +
|
|
174
|
+
` git -C "${TARGET}" log -5 --stat\n` +
|
|
175
|
+
` the existing reviews in "${reviewsDir}" — list it, and for each *.md read the header to note its ` +
|
|
176
|
+
`reviewed commit, date, and which personas it covered.\n\n` +
|
|
177
|
+
`Available lenses (prefix → file): ${personaCatalog}.\n\n` +
|
|
178
|
+
`Decide which lenses to RUN. Reason from the actual diff/changes. A change to docs/spec.md, the renderer, ` +
|
|
179
|
+
`fingerprint/keccak/colors, or the conformance suite almost always warrants SPEC; a change to colors, the ` +
|
|
180
|
+
`palette, the renderer's visual output, or the gallery/figures warrants PSY. Recommend SKIPPING a lens ONLY ` +
|
|
181
|
+
`when a recent review for it exists AND no changed file is relevant to that lens since that review's reviewed ` +
|
|
182
|
+
`commit. Be CONSERVATIVE about SPEC and SEC: skip them only when there are ZERO relevant changes since their ` +
|
|
183
|
+
`last review. Every skip MUST carry a human-readable reason.\n\n` +
|
|
184
|
+
`Return {personas: [<prefixes to RUN>], skipped: [{prefix, reason}, ...]}. Use only these prefixes: ` +
|
|
185
|
+
`${[...VALID_PREFIXES].join(', ')}.`,
|
|
186
|
+
{ label: 'scope', phase: 'Scope', schema: SCOPE_SCHEMA, agentType: 'Explore' },
|
|
187
|
+
)
|
|
188
|
+
const proposed = (scope && Array.isArray(scope.personas) ? scope.personas : []).map(toPrefix).filter((x) => VALID_PREFIXES.has(x))
|
|
189
|
+
const chosen = [...new Set(proposed)]
|
|
190
|
+
if (chosen.length === 0) {
|
|
191
|
+
wanted = DEFAULT_PREFIXES
|
|
192
|
+
log(`Scope: agent returned an empty/invalid persona set — falling back to DEFAULT_PREFIXES (${DEFAULT_PREFIXES.join(', ')})`)
|
|
193
|
+
} else {
|
|
194
|
+
wanted = chosen
|
|
195
|
+
const skips = (scope && Array.isArray(scope.skipped) ? scope.skipped : []).map((s) => `${s.prefix}: ${s.reason}`)
|
|
196
|
+
log(`Scope: running ${wanted.join(', ')}${skips.length ? `; skipped — ${skips.join(' | ')}` : ''}`)
|
|
197
|
+
}
|
|
198
|
+
PERSONAS = ALL_PERSONAS.filter((p) => wanted.includes(p.prefix))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---- Phase 1: fan the personas out against the verified target ----
|
|
202
|
+
phase('Review')
|
|
203
|
+
// Resolve each persona's effort + model: per-persona override (args.overrides[PREFIX]) beats a
|
|
204
|
+
// run-wide override (args.effort / args.model) beats the per-persona default baked into ALL_PERSONAS.
|
|
205
|
+
const personaPlan = PERSONAS.map((p) => {
|
|
206
|
+
const eff = (args.overrides && args.overrides[p.prefix] && args.overrides[p.prefix].effort) || args.effort || p.effort
|
|
207
|
+
const mdl = (args.overrides && args.overrides[p.prefix] && args.overrides[p.prefix].model) || args.model || p.model
|
|
208
|
+
return { ...p, eff, mdl }
|
|
209
|
+
})
|
|
210
|
+
log(`Review plan (persona → effort/model):\n${personaPlan.map((p) => ` ${p.prefix.padEnd(4)} → ${p.eff} / ${p.mdl}`).join('\n')}`)
|
|
211
|
+
const perPersona = await runChunked(personaPlan, CONCURRENCY, (p) =>
|
|
212
|
+
agent(
|
|
213
|
+
`TARGETING — read carefully:\n` +
|
|
214
|
+
`Review ONLY the repository at "${TARGET}" (branch "${reviewedBranch}"). Before reading any code, run ` +
|
|
215
|
+
`\`git -C "${TARGET}" rev-parse --show-toplevel\` and confirm it equals "${TARGET}"; if it does NOT, STOP and ` +
|
|
216
|
+
`return {"findings": []} with no other action. Resolve every file path under "${TARGET}" (use \`git -C "${TARGET}"\` ` +
|
|
217
|
+
`or absolute paths).\n\n` +
|
|
218
|
+
`Read your persona instructions from "${PROMPTS_DIR}/${p.file}" and execute them in UNATTENDED mode ` +
|
|
219
|
+
`(mode: unattended, effort: ${p.eff}, run_label: "${milestone}"). Run heavy shell work — and especially anything ` +
|
|
220
|
+
`that runs the renderer, the test suite, or the figure/gallery generators — under \`nice -n 19 ionice -c 3\`. ` +
|
|
221
|
+
`Use id prefix "${p.prefix}-" and the dedupe_key convention in ${PROMPTS_DIR}/orchestrating-reviews.md §3. ` +
|
|
222
|
+
`Write your full narrative report to the ABSOLUTE path "${reviewsDir}/${p.file.replace(/\.md$/, '')}-${milestone}.md" ` +
|
|
223
|
+
`(create "${reviewsDir}" if needed; do NOT git add/commit). Then return the findings manifest as structured output.`,
|
|
224
|
+
{ label: `review:${p.prefix}`, phase: 'Review', schema: FINDINGS_SCHEMA, model: p.mdl },
|
|
225
|
+
).then((r) => (r && Array.isArray(r.findings) ? r.findings : [])),
|
|
226
|
+
)
|
|
227
|
+
const raw = perPersona.filter(Boolean).flat()
|
|
228
|
+
|
|
229
|
+
// ---- Phase 1.5: pipelined-intent verification — adversarially try to REFUTE high-stakes findings ----
|
|
230
|
+
// Gated by args.verify:
|
|
231
|
+
// 'off' → skip entirely
|
|
232
|
+
// 'all' → verify all CRITICAL+HIGH recommend-fix findings
|
|
233
|
+
// unset | 'default' → verify spec-conformance / security findings OR any CRITICAL
|
|
234
|
+
const verifyMode = (args && args.verify) || 'default'
|
|
235
|
+
const VERIFY_SCHEMA = {
|
|
236
|
+
type: 'object',
|
|
237
|
+
required: ['verdict', 'note'],
|
|
238
|
+
additionalProperties: false,
|
|
239
|
+
properties: {
|
|
240
|
+
verdict: { enum: ['confirmed', 'refuted', 'uncertain'] },
|
|
241
|
+
note: { type: 'string' },
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
const verifyGate = (f) => {
|
|
245
|
+
if (verifyMode === 'all')
|
|
246
|
+
return (f.severity === 'CRITICAL' || f.severity === 'HIGH') && f.recommended_disposition === 'recommend-fix'
|
|
247
|
+
// default
|
|
248
|
+
return f.persona === 'spec-conformance-auditor' || f.persona === 'security-hawk' || f.severity === 'CRITICAL'
|
|
249
|
+
}
|
|
250
|
+
let refuted = []
|
|
251
|
+
const verificationById = new Map()
|
|
252
|
+
if (verifyMode !== 'off') {
|
|
253
|
+
phase('Verify')
|
|
254
|
+
const toVerify = raw.filter(verifyGate)
|
|
255
|
+
if (toVerify.length === 0) {
|
|
256
|
+
log(`Verify (mode: ${verifyMode}): no findings matched the gate — skipping`)
|
|
257
|
+
} else {
|
|
258
|
+
log(`Verify (mode: ${verifyMode}): adversarially checking ${toVerify.length} of ${raw.length} findings`)
|
|
259
|
+
const verifyOne = (f) => {
|
|
260
|
+
// CAPABLE verifier (Opus, full agent) for spec-conformance, security, or any CRITICAL; LIGHT read-only Explore otherwise.
|
|
261
|
+
const capable = f.persona === 'spec-conformance-auditor' || f.persona === 'security-hawk' || f.severity === 'CRITICAL'
|
|
262
|
+
const opts = { label: `verify:${f.id}`, phase: 'Verify', schema: VERIFY_SCHEMA }
|
|
263
|
+
if (capable) opts.model = 'claude-opus-4-8'
|
|
264
|
+
else opts.agentType = 'Explore'
|
|
265
|
+
return agent(
|
|
266
|
+
`TARGETING — read carefully: before doing anything, run \`git -C "${TARGET}" rev-parse --show-toplevel\` ` +
|
|
267
|
+
`and confirm it equals "${TARGET}". If it does NOT, STOP and return {"verdict":"uncertain","note":"could not confirm target repo"}. ` +
|
|
268
|
+
`Review ONLY the repository at "${TARGET}". Run heavy shell work (including running the renderer or tests) under \`nice -n 19 ionice -c 3\`.\n\n` +
|
|
269
|
+
`Your job is to ADVERSARIALLY attempt to REFUTE the following code-review finding by inspecting the actual code, the spec (docs/spec.md), and — where it settles the question — the rendered output. ` +
|
|
270
|
+
`Do NOT try to confirm it; actively look for evidence that it is wrong, already mitigated, a false positive, or not present. For a spec-conformance claim, confirm the cited normative MUST/SHALL actually exists and that the code truly diverges.\n` +
|
|
271
|
+
`Finding:\n` +
|
|
272
|
+
` id: ${f.id}\n` +
|
|
273
|
+
` title: ${f.title}\n` +
|
|
274
|
+
` location: ${f.location}\n` +
|
|
275
|
+
` severity: ${f.severity}\n` +
|
|
276
|
+
` rationale: ${f.rationale}\n\n` +
|
|
277
|
+
`Return {verdict, note} where verdict is:\n` +
|
|
278
|
+
` "confirmed" — you found concrete evidence the finding holds as stated;\n` +
|
|
279
|
+
` "refuted" — you found concrete CONTRARY evidence (the issue is absent, already mitigated, or a false positive);\n` +
|
|
280
|
+
` "uncertain" — you could not establish the claim either way.\n` +
|
|
281
|
+
`DEFAULT to "uncertain" whenever you cannot establish the claim. Mark "refuted" ONLY with concrete contrary evidence. ` +
|
|
282
|
+
`note = one line summarizing the evidence for your verdict.`,
|
|
283
|
+
opts,
|
|
284
|
+
)
|
|
285
|
+
.then((r) => ({ id: f.id, verdict: (r && r.verdict) || 'uncertain', note: (r && r.note) || '' }))
|
|
286
|
+
// A verifier that errors out or never emits its structured verdict (e.g. the subagent
|
|
287
|
+
// finishes without calling StructuredOutput) must NOT crash the run — agent({schema})
|
|
288
|
+
// rejects in that case, which parallel() turns into a null slot. Default to 'uncertain'
|
|
289
|
+
// (the safe disposition: the finding stays in the queue, unverified) and keep the id.
|
|
290
|
+
.catch(() => ({ id: f.id, verdict: 'uncertain', note: 'verifier did not return a structured verdict; defaulted to uncertain' }))
|
|
291
|
+
}
|
|
292
|
+
const verdicts = (await runChunked(toVerify, CONCURRENCY, verifyOne)).filter(Boolean)
|
|
293
|
+
for (const v of verdicts) verificationById.set(v.id, { verdict: v.verdict, note: v.note })
|
|
294
|
+
// Annotate raw findings with their verification; partition out the refuted ones.
|
|
295
|
+
for (const f of raw) {
|
|
296
|
+
const v = verificationById.get(f.id)
|
|
297
|
+
if (v) f.verification = v
|
|
298
|
+
}
|
|
299
|
+
refuted = raw.filter((f) => f.verification && f.verification.verdict === 'refuted')
|
|
300
|
+
const refutedIds = new Set(refuted.map((f) => f.id))
|
|
301
|
+
raw.splice(0, raw.length, ...raw.filter((f) => !refutedIds.has(f.id)))
|
|
302
|
+
const confirmedCount = verdicts.filter((v) => v.verdict === 'confirmed').length
|
|
303
|
+
const uncertainCount = verdicts.filter((v) => v.verdict === 'uncertain').length
|
|
304
|
+
log(`Verify: ${confirmedCount} confirmed, ${refuted.length} refuted (excluded), ${uncertainCount} uncertain`)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const verificationCounts = {
|
|
308
|
+
confirmed: [...verificationById.values()].filter((v) => v.verdict === 'confirmed').length,
|
|
309
|
+
refuted: refuted.length,
|
|
310
|
+
uncertain: [...verificationById.values()].filter((v) => v.verdict === 'uncertain').length,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ---- merge by dedupe_key (plain JS) ----
|
|
314
|
+
const SEV_RANK = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 }
|
|
315
|
+
const merged = new Map()
|
|
316
|
+
for (const f of raw) {
|
|
317
|
+
const key = f.dedupe_key
|
|
318
|
+
if (!merged.has(key)) {
|
|
319
|
+
merged.set(key, { ...f, reported_by: [f.persona], locations: [f.location] })
|
|
320
|
+
} else {
|
|
321
|
+
const m = merged.get(key)
|
|
322
|
+
if (!m.reported_by.includes(f.persona)) m.reported_by.push(f.persona)
|
|
323
|
+
if (!m.locations.includes(f.location)) m.locations.push(f.location)
|
|
324
|
+
if (SEV_RANK[f.severity] < SEV_RANK[m.severity]) m.severity = f.severity
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const items = [...merged.values()].sort((a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity])
|
|
328
|
+
log(`${raw.length} raw findings from ${PERSONAS.length} personas → ${items.length} after exact-key dedupe`)
|
|
329
|
+
|
|
330
|
+
// ---- Phase 2: synthesis — executive summary + a semantic dedup pass ----
|
|
331
|
+
phase('Synthesize')
|
|
332
|
+
const SYNTHESIS_SCHEMA = {
|
|
333
|
+
type: 'object',
|
|
334
|
+
required: ['summary', 'duplicateGroups'],
|
|
335
|
+
additionalProperties: false,
|
|
336
|
+
properties: {
|
|
337
|
+
summary: { type: 'string' },
|
|
338
|
+
duplicateGroups: {
|
|
339
|
+
type: 'array',
|
|
340
|
+
description: 'Each inner array is a set of finding ids that describe the SAME underlying issue and should merge. Singletons need not be listed.',
|
|
341
|
+
items: { type: 'array', items: { type: 'string' } },
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
const synth = await agent(
|
|
346
|
+
`You are the review orchestrator for the entviz repo at ${TARGET} (branch ${reviewedBranch}), milestone "${milestone}". ` +
|
|
347
|
+
`Here are ${items.length} findings, already merged by exact dedupe_key (JSON):\n${JSON.stringify(items)}\n\n` +
|
|
348
|
+
`Do TWO things and return both:\n` +
|
|
349
|
+
`1. "summary": a 4-6 sentence executive summary — overall posture, the milestone-blocking findings ` +
|
|
350
|
+
`(CRITICAL/HIGH with recommend-fix), and the single most urgent action. Do not restate every finding.\n` +
|
|
351
|
+
`2. "duplicateGroups": semantic deduplication. Independent personas often emit DIFFERENT dedupe_keys for ` +
|
|
352
|
+
`the SAME underlying issue (e.g. a perception finding and a spec-conformance finding both about the surround ` +
|
|
353
|
+
`channel collapsing under CVD). Judge sameness from each finding's title + location + rationale. Return an array ` +
|
|
354
|
+
`of arrays of finding "id" strings, where each inner array is a set of ids describing the SAME underlying issue. ` +
|
|
355
|
+
`Be CONSERVATIVE: only group findings that are genuinely the same issue. Singletons need not be listed; return [] if nothing should merge.`,
|
|
356
|
+
{ label: 'synthesize', phase: 'Synthesize', schema: SYNTHESIS_SCHEMA },
|
|
357
|
+
)
|
|
358
|
+
const summary = (synth && typeof synth.summary === 'string') ? synth.summary : ''
|
|
359
|
+
const duplicateGroups = (synth && Array.isArray(synth.duplicateGroups)) ? synth.duplicateGroups : []
|
|
360
|
+
|
|
361
|
+
// ---- apply the semantic merges (plain JS) → reconciled ----
|
|
362
|
+
const byId = new Map(items.map((f) => [f.id, f]))
|
|
363
|
+
const consumed = new Set()
|
|
364
|
+
const canonicalReplacing = new Map()
|
|
365
|
+
const droppedIds = new Set()
|
|
366
|
+
let semanticMerged = 0
|
|
367
|
+
for (const group of duplicateGroups) {
|
|
368
|
+
const seen = new Set()
|
|
369
|
+
const members = []
|
|
370
|
+
for (const id of Array.isArray(group) ? group : []) {
|
|
371
|
+
if (!byId.has(id) || consumed.has(id) || seen.has(id)) continue
|
|
372
|
+
seen.add(id)
|
|
373
|
+
members.push(byId.get(id))
|
|
374
|
+
}
|
|
375
|
+
if (members.length < 2) continue
|
|
376
|
+
const canonical = members.reduce((best, m) =>
|
|
377
|
+
SEV_RANK[m.severity] < SEV_RANK[best.severity] ? m : best, members[0])
|
|
378
|
+
const reportedBy = [...canonical.reported_by]
|
|
379
|
+
const locations = [...canonical.locations]
|
|
380
|
+
for (const m of members) {
|
|
381
|
+
consumed.add(m.id)
|
|
382
|
+
if (m === canonical) continue
|
|
383
|
+
for (const p of m.reported_by) if (!reportedBy.includes(p)) reportedBy.push(p)
|
|
384
|
+
for (const loc of m.locations) if (!locations.includes(loc)) locations.push(loc)
|
|
385
|
+
droppedIds.add(m.id)
|
|
386
|
+
semanticMerged++
|
|
387
|
+
}
|
|
388
|
+
canonicalReplacing.set(canonical.id, { ...canonical, reported_by: reportedBy, locations })
|
|
389
|
+
}
|
|
390
|
+
const reconciled = items
|
|
391
|
+
.filter((f) => !droppedIds.has(f.id))
|
|
392
|
+
.map((f) => canonicalReplacing.get(f.id) || f)
|
|
393
|
+
.sort((a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity])
|
|
394
|
+
log(`semantic dedup pass merged ${semanticMerged} finding(s) → ${reconciled.length} reconciled findings`)
|
|
395
|
+
|
|
396
|
+
const blockers = reconciled.filter((i) => (i.severity === 'CRITICAL' || i.severity === 'HIGH') && i.recommended_disposition === 'recommend-fix')
|
|
397
|
+
|
|
398
|
+
// ---- Phase 3: persist the synthesis to the target repo's reviews/ (durable + greppable) ----
|
|
399
|
+
phase('Persist')
|
|
400
|
+
const personaFiles = PERSONAS.map((p) => `${p.file.replace(/\.md$/, '')}-${milestone}.md`)
|
|
401
|
+
const PERSIST_SCHEMA = { type: 'object', required: ['path'], additionalProperties: false, properties: { path: { type: 'string' } } }
|
|
402
|
+
const persisted = await agent(
|
|
403
|
+
`Write a review-panel synthesis report, then return its path. Make NO source edits and do NOT git add/commit.\n` +
|
|
404
|
+
`Create the directory "${reviewsDir}" if needed, then use the Write tool to create "${reviewsDir}/review-panel-${milestone}.md" with this structure:\n` +
|
|
405
|
+
`1. A header with: target "${TARGET}", branch "${reviewedBranch}", milestone "${milestone}", today's date (get it by running: date +%F), a line "reviewed commit: ${reviewedSha || 'unknown'} (dirty working tree: ${dirty ? 'yes' : 'no'})", a line "verification: ${verificationCounts.confirmed} confirmed, ${verificationCounts.refuted} refuted, ${verificationCounts.uncertain} uncertain (verify mode: ${verifyMode})", personas reviewed (${PERSONAS.map((p) => p.prefix).join(', ')}), and counts (${raw.length} raw findings, ${reconciled.length} after dedupe, ${blockers.length} CRITICAL/HIGH recommend-fix blockers).\n` +
|
|
406
|
+
`2. "## Executive summary" containing verbatim:\n${summary}\n` +
|
|
407
|
+
`3. "## All findings" — a markdown table of every finding, sorted severity CRITICAL→LOW, columns: id | severity | confidence | location | disposition | effort | reported_by | title.\n` +
|
|
408
|
+
`4. "## Per-persona reports" — a bullet list naming these sibling files in the same directory (a file may be absent if that persona found nothing): ${personaFiles.join(', ')}.\n` +
|
|
409
|
+
`5. "## Refuted (excluded from findings)" — a markdown table of findings that adversarial verification REFUTED and that were therefore excluded from the findings above (so nothing is silently lost), columns: id | title | location | refutation note. Use exactly these rows (empty table is fine if none):\n${JSON.stringify(refuted.map((f) => ({ id: f.id, title: f.title, location: f.location, note: f.verification ? f.verification.note : '' })))}\n` +
|
|
410
|
+
`6. "## Machine-readable manifest" — a fenced JSON code block (triple-backtick, language json) containing exactly this findings array so a future session can parse it without re-running:\n${JSON.stringify(reconciled)}\n` +
|
|
411
|
+
`Return {path: "<the absolute path you wrote>"}.`,
|
|
412
|
+
{ label: 'persist', phase: 'Persist', schema: PERSIST_SCHEMA },
|
|
413
|
+
)
|
|
414
|
+
const reportPath = persisted && persisted.path ? persisted.path : `${reviewsDir}/review-panel-${milestone}.md`
|
|
415
|
+
log(`Synthesis report written to ${reportPath}`)
|
|
416
|
+
|
|
417
|
+
return { target: TARGET, branch: reviewedBranch, reviewedSha, dirty, milestone, reportPath, reviewsDir, personaReports: personaFiles, personaCount: PERSONAS.length, rawCount: raw.length, blockerCount: blockers.length, items: reconciled, summary, verification: verificationCounts, refuted }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# GitHub Copilot Instructions — entviz
|
|
2
|
+
|
|
3
|
+
**STOP!** Before making any changes, read [AGENTS.md](AGENTS.md) at the root of this repository.
|
|
4
|
+
It contains the mandatory AI orientation and behavioral rules for this project, including:
|
|
5
|
+
|
|
6
|
+
- **Comprehension First:** Demonstrate understanding of *why* before writing *how*.
|
|
7
|
+
- **Intent Layer:** Maintain and update [this.i](this.i) for all design decisions.
|
|
8
|
+
- **Strict TDD:** Never modify code without a failing test.
|
|
9
|
+
- **Naming & Metaphor:** Treat code as human language; prioritize high-quality names.
|
|
10
|
+
|
|
11
|
+
**The short version:** Never modify code without first verifying the existing tests pass, and never leave code in a state where the tests do not pass. Ensure all logic adheres to the Entviz algorithm specification in [docs/index.md](docs/index.md).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
# GitHub Actions used by the CI and release workflows. Actions are pinned to
|
|
5
|
+
# full commit SHAs; Dependabot bumps each SHA and its "# vN.N.N" comment
|
|
6
|
+
# together. Group all action bumps into a single PR so a weekly batch of SHA
|
|
7
|
+
# updates doesn't fan out into one PR per action.
|
|
8
|
+
- package-ecosystem: "github-actions"
|
|
9
|
+
directory: "/"
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly"
|
|
12
|
+
labels: ["dependencies", "github-actions"]
|
|
13
|
+
groups:
|
|
14
|
+
actions:
|
|
15
|
+
patterns: ["*"]
|
|
16
|
+
|
|
17
|
+
# Python dependencies. Use the native "uv" ecosystem (GA March 2025) rather
|
|
18
|
+
# than "pip": only the uv ecosystem regenerates uv.lock alongside
|
|
19
|
+
# pyproject.toml, keeping the lockfile in sync (the pip ecosystem updates
|
|
20
|
+
# pyproject.toml but leaves uv.lock stale, which fails CI's --locked check).
|
|
21
|
+
- package-ecosystem: "uv"
|
|
22
|
+
directory: "/"
|
|
23
|
+
schedule:
|
|
24
|
+
interval: "weekly"
|
|
25
|
+
labels: ["dependencies", "python"]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Repository rulesets (infrastructure-as-code)
|
|
2
|
+
|
|
3
|
+
Branch and tag protection for this repo lives here as version-controlled JSON
|
|
4
|
+
so it is **auditable and reviewable** rather than existing only in the GitHub
|
|
5
|
+
UI (closes review finding OPS-F3). Each `<name>.json` is a ruleset payload in
|
|
6
|
+
the shape accepted by the [rulesets REST API][api].
|
|
7
|
+
|
|
8
|
+
## Files
|
|
9
|
+
|
|
10
|
+
- **`require-review-contributors.json`** — protects the default branch (`main`):
|
|
11
|
+
blocks deletion and force-pushes (`non_fast_forward`), requires a pull request
|
|
12
|
+
with 1 approving review (stale reviews dismissed on push, last-push approval
|
|
13
|
+
required), and requires the CI checks `unicode-guard` and `test (3.10|3.11|3.12)`
|
|
14
|
+
to pass with the strict (up-to-date-branch) policy. The repository **Write**
|
|
15
|
+
role (`bypass_actors`, role id 2) may bypass, so the solo maintainer can still
|
|
16
|
+
push directly — tighten this when more contributors join.
|
|
17
|
+
- **`protect-release-tags.json`** — protects release tags (`refs/tags/v*`,
|
|
18
|
+
consumed by `release.yml`) from deletion and from being moved
|
|
19
|
+
(`non_fast_forward`). Creating a *new* `v*` tag (the normal release flow) is
|
|
20
|
+
unaffected; only deleting or force-updating an existing one is blocked. Same
|
|
21
|
+
Write-role bypass.
|
|
22
|
+
|
|
23
|
+
## Applying / updating
|
|
24
|
+
|
|
25
|
+
The committed JSON is the source of truth. After editing a file (or to
|
|
26
|
+
bootstrap a fresh clone of the protection), run:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
./.github/rulesets/apply.sh # defaults to dhh1128/entviz
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
It is idempotent — it updates the existing ruleset (matched by `name`) or
|
|
33
|
+
creates it if absent. Requires an authenticated `gh` and `jq`.
|
|
34
|
+
|
|
35
|
+
> As of this commit the committed JSON matches the live rulesets (both have
|
|
36
|
+
> been applied), so no apply is needed right now; the script is for future
|
|
37
|
+
> changes and fresh setups.
|
|
38
|
+
|
|
39
|
+
## Keeping in sync (detecting drift)
|
|
40
|
+
|
|
41
|
+
If protection is changed in the UI, re-export so the repo reflects reality:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
gh api repos/dhh1128/entviz/rulesets/<id> \
|
|
45
|
+
| jq '{name, target, enforcement, bypass_actors, conditions, rules}' \
|
|
46
|
+
> .github/rulesets/<name>.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
then commit the diff. `gh api repos/dhh1128/entviz/rulesets` lists ids/names.
|
|
50
|
+
|
|
51
|
+
## Possible future additions
|
|
52
|
+
|
|
53
|
+
- **CodeQL as a required check.** `Analyze (actions)` / `Analyze (python)` run
|
|
54
|
+
on PRs but are not blocking. Add them to `required_status_checks` if you want
|
|
55
|
+
security analysis to gate merges.
|
|
56
|
+
- **Code-owner review.** `require_code_owner_review` is off and there is no
|
|
57
|
+
`CODEOWNERS` file; add both if review routing becomes useful.
|
|
58
|
+
|
|
59
|
+
[api]: https://docs.github.com/en/rest/repos/rules
|