assurance-cli 0.2.2__tar.gz → 0.2.3__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.
Files changed (24) hide show
  1. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/PKG-INFO +8 -2
  2. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/README.md +6 -0
  3. assurance_cli-0.2.3/assurance_cli/__init__.py +19 -0
  4. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/baseline.py +22 -2
  5. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/cli.py +12 -2
  6. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/PKG-INFO +8 -2
  7. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/SOURCES.txt +2 -1
  8. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/requires.txt +1 -1
  9. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/pyproject.toml +2 -2
  10. assurance_cli-0.2.3/tests/test_version.py +33 -0
  11. assurance_cli-0.2.2/assurance_cli/__init__.py +0 -8
  12. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/LICENSE +0 -0
  13. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/__main__.py +0 -0
  14. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/gather.py +0 -0
  15. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/paths.py +0 -0
  16. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/profile.py +0 -0
  17. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli/setdiff.py +0 -0
  18. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/dependency_links.txt +0 -0
  19. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/entry_points.txt +0 -0
  20. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/assurance_cli.egg-info/top_level.txt +0 -0
  21. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/setup.cfg +0 -0
  22. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/tests/test_cli.py +0 -0
  23. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/tests/test_read_only.py +0 -0
  24. {assurance_cli-0.2.2 → assurance_cli-0.2.3}/tests/test_setdiff.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assurance-cli
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: CLI for folder assurance checks — coverage, staleness, and baselines.
5
5
  Author-email: "I-Ops Operations Intelligence, LLC" <hello@i-ops.dev>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
19
19
  Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: assurance-core>=0.3.2
22
+ Requires-Dist: assurance-core>=0.3.3
23
23
  Requires-Dist: openpyxl>=3.1
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: pytest>=8.0; extra == "dev"
@@ -56,6 +56,10 @@ No account · no API key · no network call · **no model decides any of it**
56
56
 
57
57
  ## Three commands
58
58
 
59
+ **`diff` is the general one.** `check` is the special case for a folder of
60
+ dated or numbered *tabular* files — if your files are `.md`, or named in a format it can't read, use
61
+ `diff` and declare the set yourself.
62
+
59
63
  ### `assurance diff` — any two sets of keys
60
64
 
61
65
  ```bash
@@ -99,6 +103,8 @@ every ratio so you can disagree with the denominator, not just the result.
99
103
 
100
104
  ```bash
101
105
  assurance init ~/thesis-data
106
+ # Baseline written to ~/thesis-data/.assurance.json — 34 tabular files recorded.
107
+
102
108
  # ... weeks pass, several people touch the folder ...
103
109
  assurance check ~/thesis-data --against-baseline
104
110
  ```
@@ -29,6 +29,10 @@ No account · no API key · no network call · **no model decides any of it**
29
29
 
30
30
  ## Three commands
31
31
 
32
+ **`diff` is the general one.** `check` is the special case for a folder of
33
+ dated or numbered *tabular* files — if your files are `.md`, or named in a format it can't read, use
34
+ `diff` and declare the set yourself.
35
+
32
36
  ### `assurance diff` — any two sets of keys
33
37
 
34
38
  ```bash
@@ -72,6 +76,8 @@ every ratio so you can disagree with the denominator, not just the result.
72
76
 
73
77
  ```bash
74
78
  assurance init ~/thesis-data
79
+ # Baseline written to ~/thesis-data/.assurance.json — 34 tabular files recorded.
80
+
75
81
  # ... weeks pass, several people touch the folder ...
76
82
  assurance check ~/thesis-data --against-baseline
77
83
  ```
@@ -0,0 +1,19 @@
1
+ """assurance-cli — folder checks anyone can run without I-Ops.
2
+
3
+ Shared filesystem I/O (`paths`, `profile`, `gather`) lives in this package so `assurance-mcp` can
4
+ import it instead of duplicating the profiler. MCP depends on this package for gathering only; the
5
+ CLI does not depend on MCP. Two copies of the profiler is how they disagree.
6
+ """
7
+
8
+ # Read from the installed distribution rather than typed here. A hardcoded literal is a second place
9
+ # the version lives, and the second place is the one that goes stale: these sat at "0.1.0" through
10
+ # five releases and were reported by an outside reviewer on 2026-08-29. One source of truth is
11
+ # `pyproject.toml`, and this reads what was actually installed from it.
12
+ from importlib.metadata import PackageNotFoundError, version as _version
13
+
14
+ try:
15
+ __version__ = _version("assurance-cli")
16
+ except PackageNotFoundError: # running from a source tree that was never installed
17
+ __version__ = "0.0.0+unknown"
18
+
19
+ __all__ = ["__version__"]
@@ -19,7 +19,12 @@ def init_baseline(folder: str, *, update: bool = False) -> dict[str, Any]:
19
19
  root = resolve_folder(folder)
20
20
  path = root / BASELINE_NAME
21
21
  if path.exists() and not update:
22
- return {"written": False, "path": str(path), "reason": "baseline already exists; use --update"}
22
+ return {
23
+ "written": False,
24
+ "path": str(path),
25
+ "reason": "baseline already exists; use --update",
26
+ "summary": f"A baseline already exists at {path}. Pass --update to overwrite it.",
27
+ }
23
28
 
24
29
  entries: dict[str, Any] = {}
25
30
  for file_path in sorted(root.rglob("*")):
@@ -50,7 +55,22 @@ def init_baseline(folder: str, *, update: bool = False) -> dict[str, Any]:
50
55
  "files": entries,
51
56
  }
52
57
  path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
53
- return {"written": True, "path": str(path), "file_count": len(entries)}
58
+ # A summary, because text mode renders one and prints nothing without it. `assurance init`
59
+ # wrote the file and then said nothing at all, so success was indistinguishable from a hung
60
+ # command — reported by an outside reviewer 2026-08-29. The count is the useful half: a baseline
61
+ # over zero files is a baseline that will never notice anything.
62
+ noun = "file" if len(entries) == 1 else "files"
63
+ return {
64
+ "written": True,
65
+ "path": str(path),
66
+ "file_count": len(entries),
67
+ "summary": (
68
+ f"Baseline written to {path} — {len(entries)} tabular {noun} recorded. "
69
+ "Re-check with `assurance check <folder> --against-baseline`."
70
+ if entries
71
+ else f"Baseline written to {path} — no tabular files found, so it records nothing."
72
+ ),
73
+ }
54
74
 
55
75
 
56
76
  def check_against_baseline(folder: str) -> dict[str, Any]:
@@ -14,14 +14,24 @@ from assurance_cli.setdiff import KeySpecError, diff_sets, format_diff
14
14
 
15
15
 
16
16
  def main(argv: list[str] | None = None) -> int:
17
- parser = argparse.ArgumentParser(prog="assurance", description="Folder assurance checks — arithmetic, not models.")
17
+ parser = argparse.ArgumentParser(
18
+ prog="assurance",
19
+ description=(
20
+ "Did the job cover everything it was supposed to cover? Arithmetic, not models. "
21
+ "`diff` is the general command — it compares any two sets of keys. `check` is the "
22
+ "special case for a folder of dated or numbered files."
23
+ ),
24
+ )
18
25
  sub = parser.add_subparsers(dest="command", required=True)
19
26
 
20
27
  init_parser = sub.add_parser("init", help="Write .assurance.json baseline")
21
28
  init_parser.add_argument("folder", help="Folder to baseline")
22
29
  init_parser.add_argument("--update", action="store_true", help="Overwrite an existing baseline")
23
30
 
24
- check_parser = sub.add_parser("check", help="Check a folder for coverage or staleness")
31
+ check_parser = sub.add_parser(
32
+ "check",
33
+ help="Special case: a folder of DATED or NUMBERED files (tabular). For anything else use `diff`",
34
+ )
25
35
  check_parser.add_argument("folder", help="Folder to check")
26
36
  check_parser.add_argument("--expect", choices=["monthly", "quarterly", "weekly", "daily", "numbered"])
27
37
  check_parser.add_argument("--from", dest="from_point", metavar="FROM")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assurance-cli
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: CLI for folder assurance checks — coverage, staleness, and baselines.
5
5
  Author-email: "I-Ops Operations Intelligence, LLC" <hello@i-ops.dev>
6
6
  License: Apache-2.0
@@ -19,7 +19,7 @@ Classifier: Topic :: Software Development :: Quality Assurance
19
19
  Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: assurance-core>=0.3.2
22
+ Requires-Dist: assurance-core>=0.3.3
23
23
  Requires-Dist: openpyxl>=3.1
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: pytest>=8.0; extra == "dev"
@@ -56,6 +56,10 @@ No account · no API key · no network call · **no model decides any of it**
56
56
 
57
57
  ## Three commands
58
58
 
59
+ **`diff` is the general one.** `check` is the special case for a folder of
60
+ dated or numbered *tabular* files — if your files are `.md`, or named in a format it can't read, use
61
+ `diff` and declare the set yourself.
62
+
59
63
  ### `assurance diff` — any two sets of keys
60
64
 
61
65
  ```bash
@@ -99,6 +103,8 @@ every ratio so you can disagree with the denominator, not just the result.
99
103
 
100
104
  ```bash
101
105
  assurance init ~/thesis-data
106
+ # Baseline written to ~/thesis-data/.assurance.json — 34 tabular files recorded.
107
+
102
108
  # ... weeks pass, several people touch the folder ...
103
109
  assurance check ~/thesis-data --against-baseline
104
110
  ```
@@ -17,4 +17,5 @@ assurance_cli.egg-info/requires.txt
17
17
  assurance_cli.egg-info/top_level.txt
18
18
  tests/test_cli.py
19
19
  tests/test_read_only.py
20
- tests/test_setdiff.py
20
+ tests/test_setdiff.py
21
+ tests/test_version.py
@@ -1,4 +1,4 @@
1
- assurance-core>=0.3.2
1
+ assurance-core>=0.3.3
2
2
  openpyxl>=3.1
3
3
 
4
4
  [dev]
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "assurance-cli"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "CLI for folder assurance checks — coverage, staleness, and baselines."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
11
11
  authors = [{ name = "I-Ops Operations Intelligence, LLC", email = "hello@i-ops.dev" }]
12
12
  requires-python = ">=3.10"
13
13
  dependencies = [
14
- "assurance-core>=0.3.2",
14
+ "assurance-core>=0.3.3",
15
15
  "openpyxl>=3.1",
16
16
  ]
17
17
  keywords = ["cli", "ai-agents", "governance", "auditing", "assurance", "reproducibility"]
@@ -0,0 +1,33 @@
1
+ """`__version__` must agree with `pyproject.toml`.
2
+
3
+ Both were hardcoded at "0.1.0" and sat there through five releases, so anyone reading
4
+ `assurance_cli.__version__` was told the wrong thing. Reported by an outside reviewer 2026-08-29.
5
+
6
+ They now read the installed distribution's metadata, which makes `pyproject.toml` the only place a
7
+ version is written. This is the guard that the two agree: CI installs the package immediately before
8
+ running the suite, so the metadata is current there. Locally it can lag a version behind after an
9
+ edit — `pip install -e .` resyncs it, and a lagging editable install is exactly the drift worth
10
+ being told about.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import re
16
+ from pathlib import Path
17
+
18
+ import assurance_cli
19
+
20
+
21
+ def _declared() -> str:
22
+ text = (Path(__file__).resolve().parent.parent / "pyproject.toml").read_text(encoding="utf-8")
23
+ match = re.search(r'^version = "([^"]+)"', text, re.MULTILINE)
24
+ assert match, "pyproject.toml has no version"
25
+ return match.group(1)
26
+
27
+
28
+ def test_version_agrees_with_pyproject() -> None:
29
+ assert assurance_cli.__version__ == _declared()
30
+
31
+
32
+ def test_version_is_not_a_placeholder() -> None:
33
+ assert assurance_cli.__version__ != "0.0.0+unknown"
@@ -1,8 +0,0 @@
1
- """assurance-cli — folder checks anyone can run without I-Ops.
2
-
3
- Shared filesystem I/O (`paths`, `profile`, `gather`) lives in this package so `assurance-mcp` can
4
- import it instead of duplicating the profiler. MCP depends on this package for gathering only; the
5
- CLI does not depend on MCP. Two copies of the profiler is how they disagree.
6
- """
7
-
8
- __version__ = "0.1.0"
File without changes
File without changes