diff --git a/AGENTS.md b/AGENTS.md index fd47d95..cf7df8c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,7 +61,7 @@ avoid comments or tests that restate the implementation. Add or update concise docstrings when changing public behavior. Write codex32 in lowercase except when referring to the Codex32 Book. -Keep the installed package below 5,200 logical review lines, as enforced by the +Keep the installed package below 5,250 logical review lines, as enforced by the existing test. New dependencies, public API signature or return-shape changes, and lint suppressions require user authorization; an explicit request can already provide that authorization. diff --git a/docs/developer/api.md b/docs/developer/api.md index 4e6cd06..dadf99f 100644 --- a/docs/developer/api.md +++ b/docs/developer/api.md @@ -105,7 +105,7 @@ unsupported but remains in the review scope. ### Size budget -V1 keeps the installed package below 5,200 logical review lines, excluding +V1 keeps the installed package below 5,250 logical review lines, excluding blank and comment-only lines while counting subpackages recursively. Changing the budget requires explicit review and authorization together with the matching documentation and enforcement update. diff --git a/src/codex32/cli.py b/src/codex32/cli.py index 6e81e43..a23c3a5 100644 --- a/src/codex32/cli.py +++ b/src/codex32/cli.py @@ -189,6 +189,10 @@ def _share_command(index: str, plain: bool, context: _CliContext, core: BitcoinC derived = derive_share(artifacts, index) except CodexError as error: raise _CommandError(str(error)) from error + if core is not None and sys.stdout.isatty() and not plain: + given = [artifact for artifact in artifacts if isinstance(artifact, Secret)] + seed = cast(MasterSeed, given[0] if given else _secret(artifacts)) + _print(f"Master fingerprint: {core.fingerprint(seed).hex().upper()}\n") _emit(derived, plain) if sys.stdin.isatty() and sys.stdout.isatty() and not plain: try: diff --git a/tests/test_cli.py b/tests/test_cli.py index 889b175..1aabfc4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1693,13 +1693,15 @@ def test_create_rejects_bip39_partial_basis_and_selector_conflicts() -> None: assert result.exit_code != 0 -def test_terminal_secret_has_fingerprint_but_share_does_not() -> None: - secret = _invoke_terminal(["secret"], VECTOR_1["secret_s"]) +def test_terminal_secret_and_share_show_the_master_fingerprint() -> None: + secret = _invoke_terminal(["secret"], VECTOR_2["share_A"], VECTOR_2["share_C"]) share = _invoke_terminal(["share", "d"], VECTOR_2["share_A"], VECTOR_2["share_C"]) + from_secret = _invoke_terminal(["share", "d"], VECTOR_2["secret_S"], VECTOR_2["share_C"]) + fingerprint = _FakeBitcoinCore().fingerprint(cast(MasterSeed, parse_codex32(VECTOR_2["secret_S"]))) + line = f"Master fingerprint: {fingerprint.hex().upper()}" - assert secret.exit_code == share.exit_code == 0 - assert "Master fingerprint:" in secret.stdout - assert "Master fingerprint:" not in share.stdout + assert secret.exit_code == share.exit_code == from_secret.exit_code == 0 + assert line in secret.stdout and line in share.stdout and line in from_secret.stdout assert "Backup identifier:" in secret.stdout and "Backup identifier:" in share.stdout @@ -2193,7 +2195,7 @@ def test_production_size_budgets_are_enforced() -> None: for path in package.rglob("*.py") } - assert sum(counts.values()) < 5200, counts + assert sum(counts.values()) < 5250, counts @pytest.mark.parametrize(