Agent Cost Report Claude-Mem / Claude Code skill. Runtime is the pipeline (transcripts → tokens → dollars → Timing-style report) plus a progressive Mem Search review pass that confirms the drafted labels. The Notion draft is SPEC history only — never the product, never the runtime, never the ship vehicle. Resolve the absolute directory containing this ; all helper paths are relative to that directory. is the shortcut: . Python 3.9+ standard library only, with IANA timezone data for ; no pip installs. is the only exception to the no-pip-installs rule, and only when exits saying that timezone d…

, result.stderr, re.MULTILINE)\n self.assertIsNotNone(command)\n self.assertTrue(os.path.samefile(command.group(1), sys.executable))\n # PowerShell needs the call operator, so Windows gets a second line for it.\n powershell_line = f'PowerShell: & \"{command.group(1)}\" -m pip install tzdata'\n self.assertEqual(powershell_line in result.stderr.splitlines(), os.name == \"nt\")\n # The zero-install route the #4250 reporter used.\n self.assertIn(\"PYTHONTZPATH\", result.stderr)\n\n def test_windows_install_command_handles_spaces_in_interpreter_path(self):\n with open(_paths.ACR_PY, encoding=\"utf-8\") as script:\n code = compile(script.read(), _paths.ACR_PY, \"exec\")\n real_import = builtins.__import__\n\n def missing_timezone_import(name, *args, **kwargs):\n if name == \"acr\":\n raise ZoneInfoNotFoundError(\"America/Los_Angeles\")\n return real_import(name, *args, **kwargs)\n\n interpreter = r\"C:\\Program Files\\Python\\python.exe\"\n # Exercise the CLI's error handler on every host, including its Windows branch.\n with patch(\"builtins.__import__\", side_effect=missing_timezone_import), \\\n patch(\"os.name\", \"nt\"), patch(\"sys.executable\", interpreter):\n with self.assertRaises(SystemExit) as error:\n exec(code, {\"__file__\": _paths.ACR_PY, \"__name__\": \"__main__\"})\n lines = str(error.exception).splitlines()\n plain_line = '\"C:\\\\Program Files\\\\Python\\\\python.exe\" -m pip install tzdata'\n # cmd and Git Bash (Claude Code's Bash tool on Windows) run the plain quoted line;\n # PowerShell needs the & call operator, so it gets a line of its own.\n self.assertIn(plain_line, lines)\n self.assertIn(\"PowerShell: & \" + plain_line, lines)\n self.assertIn(\"PYTHONTZPATH\", lines[-1])\n\n\nclass DefaultWindow(unittest.TestCase):\n def test_default_last_7_full_pt_days_fixed_clock(self):\n now = dt.datetime(2026, 9, 25, 16, 42, tzinfo=PT)\n w = period.resolve_window(now=now)\n self.assertEqual(w.start_pt, \"2026-09-18\")\n self.assertEqual(w.end_exclusive_pt, \"2026-09-25\")\n self.assertFalse(w.partial_last_day)\n self.assertEqual(w.generated_at_pt, \"2026-09-25 16:42 PT\")\n self.assertEqual(w.start.utcoffset(), dt.timedelta(hours=-7)) # PDT, from zoneinfo\n self.assertEqual(w.end_epoch_ms - w.start_epoch_ms, 7 * 24 * H)\n b = w.block()\n self.assertEqual(set(b), {\"start_pt\", \"end_exclusive_pt\", \"start_epoch_ms\", \"end_epoch_ms\",\n \"generated_at_pt\", \"partial_last_day\"})\n self.assertEqual(b[\"start_epoch_ms\"], int(dt.datetime(2026, 9, 18, tzinfo=PT).timestamp() * 1000))\n\n def test_default_accepts_a_utc_clock(self):\n now = dt.datetime(2026, 9, 26, 3, 0, tzinfo=dt.timezone.utc) # still 2026-09-25 20:00 PT\n w = period.resolve_window(now=now)\n self.assertEqual((w.start_pt, w.end_exclusive_pt), (\"2026-09-18\", \"2026-09-25\"))\n\n def test_explicit_end_after_today_is_partial(self):\n now = dt.datetime(2026, 9, 25, 16, 42, tzinfo=PT)\n w = period.resolve_window(\"2026-09-18\", \"2026-09-26\", now=now)\n self.assertTrue(w.partial_last_day)\n self.assertEqual(w.end_epoch_ms - w.start_epoch_ms, 8 * 24 * H)\n\n def test_explicit_end_equal_today_is_not_partial(self):\n now = dt.datetime(2026, 9, 25, 16, 42, tzinfo=PT)\n self.assertFalse(period.resolve_window(\"2026-09-18\", \"2026-09-25\", now=now).partial_last_day)\n\n def test_only_start_given_defaults_end_to_today(self):\n now = dt.datetime(2026, 9, 25, 9, 0, tzinfo=PT)\n w = period.resolve_window(start=\"2026-09-01\", now=now)\n self.assertEqual((w.start_pt, w.end_exclusive_pt), (\"2026-09-01\", \"2026-09-25\"))\n\n def test_bad_inputs(self):\n now = dt.datetime(2026, 9, 25, 9, 0, tzinfo=PT)\n with self.assertRaises(ValueError):\n period.resolve_window(\"2026-09-25\", \"2026-09-25\", now=now)\n with self.assertRaises(ValueError):\n period.parse_day(\"2026-09-25T10:00\")\n with self.assertRaises(ValueError):\n period.parse_day(\"2026-09-25+00:00\")\n\n\nclass DstBoundary(unittest.TestCase):\n \"\"\"US DST ends Sunday 2026-11-01 02:00 PT; PT windows are calendar days, so epoch spans change.\"\"\"\n\n def test_default_window_across_fall_back(self):\n now = dt.datetime(2026, 11, 4, 10, 0, tzinfo=PT)\n w = period.resolve_window(now=now)\n self.assertEqual((w.start_pt, w.end_exclusive_pt), (\"2026-10-28\", \"2026-11-04\"))\n self.assertEqual(w.start.utcoffset(), dt.timedelta(hours=-7)) # PDT\n self.assertEqual(w.end.utcoffset(), dt.timedelta(hours=-8)) # PST\n self.assertEqual(w.end_epoch_ms - w.start_epoch_ms, (7 * 24 + 1) * H)\n self.assertFalse(w.partial_last_day)\n\n def test_single_day_containing_fall_back_is_25_hours(self):\n w = period.resolve_window(\"2026-11-01\", \"2026-11-02\", now=dt.datetime(2026, 11, 5, tzinfo=PT))\n self.assertEqual(w.end_epoch_ms - w.start_epoch_ms, 25 * H)\n self.assertEqual(w.start.isoformat(), \"2026-11-01T00:00:00-07:00\")\n self.assertEqual(w.end.isoformat(), \"2026-11-02T00:00:00-08:00\")\n\n def test_default_window_ending_on_dst_day(self):\n now = dt.datetime(2026, 11, 1, 12, 0, tzinfo=PT) # afternoon of the switch day (PST)\n w = period.resolve_window(now=now)\n self.assertEqual((w.start_pt, w.end_exclusive_pt), (\"2026-10-25\", \"2026-11-01\"))\n self.assertEqual(w.end.utcoffset(), dt.timedelta(hours=-7)) # midnight was still PDT\n self.assertEqual(w.end_epoch_ms - w.start_epoch_ms, 7 * 24 * H)\n\n\nclass SessionBlock(unittest.TestCase):\n def test_session_block_has_null_bounds(self):\n b = period.session_block(\"abc\", now=dt.datetime(2026, 9, 25, 16, 42, tzinfo=PT))\n self.assertIsNone(b[\"start_pt\"]); self.assertIsNone(b[\"end_epoch_ms\"])\n self.assertEqual(b[\"session\"], \"abc\"); self.assertFalse(b[\"partial_last_day\"])\n self.assertEqual(b[\"generated_at_pt\"], \"2026-09-25 16:42 PT\")\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":7200,"content_sha256":"c4b9b88868a3513ae4884ee58e3532695ed0c7a57f7f71e9d19ceb71ac7e9b30"},{"filename":"scripts/tests/test_prices.py","content":"import json\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\n\nimport _paths\nfrom acr import prices\n\nSAMPLE = os.path.join(_paths.FIXTURES, \"prices_sample.json\")\n\n\nclass Norm(unittest.TestCase):\n def test_norm_rules(self):\n self.assertEqual(prices.norm(\"claude-fable-5-1\"), \"anthropic/claude-fable-5.1\")\n self.assertEqual(prices.norm(\"claude-sonnet-4-5-20250929\"), \"anthropic/claude-sonnet-4.5\")\n self.assertEqual(prices.norm(\"gpt-5-codex\"), \"openai/gpt-5-codex\")\n self.assertEqual(prices.norm(\"gemini-2.5-pro\"), \"google/gemini-2.5-pro\")\n self.assertEqual(prices.norm(\"anthropic/claude-opus-5.5\"), \"anthropic/claude-opus-5.5\")\n self.assertEqual(prices.norm(\"alias-x\", aliases={\"alias-x\": \"vendor/real\"}), \"vendor/real\")\n\n\nclass Rate(unittest.TestCase):\n @classmethod\n def setUpClass(cls):\n cls.models = prices.load(SAMPLE)[\"models\"]\n\n def test_explicit_cache_write_1h_is_used(self):\n r = prices.rate(\"anthropic/claude-opus-5.5\", self.models)\n self.assertEqual(r[\"cache_write_1h\"], 30.0)\n self.assertEqual(r[\"price_source\"][\"cache_write_1h\"], \"listed\")\n self.assertEqual(r[\"price_source\"], {\"cache_read\": \"listed\", \"cache_write\": \"listed\", \"cache_write_1h\": \"listed\"})\n\n def test_cache_write_1h_falls_back_to_2x_input(self):\n r = prices.rate(\"claude-fable-5-1\", self.models)\n self.assertEqual(r[\"key\"], \"anthropic/claude-fable-5.1\")\n self.assertEqual(r[\"input_usd_per_mtok\"], 10.0)\n self.assertEqual(r[\"output_usd_per_mtok\"], 50.0)\n self.assertEqual(r[\"cache_read\"], 0.25)\n self.assertEqual(r[\"cache_write\"], 12.5)\n self.assertEqual(r[\"cache_write_1h\"], 20.0)\n self.assertEqual(r[\"price_source\"][\"cache_write_1h\"], \"input*2\")\n self.assertEqual(r[\"source\"], \"openrouter-public-list\")\n\n def test_cache_read_and_write_fallbacks(self):\n r = prices.rate(\"claude-sonnet-4-5-20250929\", self.models)\n self.assertAlmostEqual(r[\"cache_read\"], 0.3)\n self.assertAlmostEqual(r[\"cache_write\"], 3.75)\n self.assertEqual(r[\"cache_write_1h\"], 6.0)\n self.assertEqual(r[\"price_source\"], {\"cache_read\": \"input*0.1\", \"cache_write\": \"input*1.25\", \"cache_write_1h\": \"input*2\"})\n\n def test_unpriced(self):\n self.assertIsNone(prices.rate(\"\u003csynthetic>\", self.models))\n self.assertIsNone(prices.rate(\"\", self.models))\n self.assertIsNone(prices.rate(None, self.models))\n self.assertIsNone(prices.rate(\"unknown-model-9\", self.models))\n self.assertIsNone(prices.rate(\"gpt-5-codex\", self.models)) # listed but input is null\n\n\nclass Files(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr-prices-\")\n\n def tearDown(self):\n shutil.rmtree(self.tmp, ignore_errors=True)\n\n def test_ensure_with_saved_prices_writes_outdir_copy(self):\n path, p = prices.ensure(self.tmp, prices_file=SAMPLE)\n self.assertEqual(path, os.path.join(self.tmp, \"prices.json\"))\n with open(path) as fh:\n d = json.load(fh)\n self.assertEqual(d[\"models\"][\"anthropic/claude-fable-5.1\"][\"input\"], 10.0)\n self.assertEqual(d[\"source\"], prices.SOURCE_URL)\n self.assertEqual(d[\"loaded_from\"], os.path.abspath(SAMPLE))\n\n def test_offline_fetch_is_an_error_not_zero(self):\n with self.assertRaises(prices.PriceError):\n prices.ensure(self.tmp, url=\"http://127.0.0.1:9/models\") # nothing listens here\n self.assertFalse(os.path.exists(os.path.join(self.tmp, \"prices.json\")))\n\n def test_bad_prices_file(self):\n bad = os.path.join(self.tmp, \"bad.json\")\n with open(bad, \"w\") as fh:\n fh.write('{\"nope\": 1}')\n with self.assertRaises(prices.PriceError):\n prices.load(bad)\n with self.assertRaises(prices.PriceError):\n prices.load(os.path.join(self.tmp, \"missing.json\"))\n\n\nclass Cli(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr-cli-\")\n\n def tearDown(self):\n shutil.rmtree(self.tmp, ignore_errors=True)\n\n def run_acr(self, *argv):\n return subprocess.run([sys.executable, _paths.ACR_PY, *argv], capture_output=True, text=True)\n\n def test_prices_offline_exits_nonzero_with_clear_error(self):\n r = self.run_acr(\"prices\", \"--out\", self.tmp, \"--url\", \"http://127.0.0.1:9/models\")\n self.assertNotEqual(r.returncode, 0)\n self.assertIn(\"could not fetch\", r.stderr)\n self.assertIn(\"--prices\", r.stderr)\n\n def test_prices_from_saved_file(self):\n r = self.run_acr(\"prices\", \"--out\", self.tmp, \"--prices\", SAMPLE)\n self.assertEqual(r.returncode, 0, r.stderr)\n self.assertIn(\"4 models\", r.stdout)\n self.assertTrue(os.path.exists(os.path.join(self.tmp, \"prices.json\")))\n\n def test_every_subcommand_is_wired(self):\n r = self.run_acr(\"--help\")\n for cmd in (\"collect\", \"prices\", \"rollup\", \"review\", \"render\", \"pdf\", \"measure-openrouter\", \"sync-check\"): self.assertIn(cmd, r.stdout, cmd)\n\n def test_collect_rejects_session_with_period(self):\n r = self.run_acr(\"collect\", \"--session\", \"x\", \"--start\", \"2026-09-18\", \"--out\", self.tmp)\n self.assertNotEqual(r.returncode, 0)\n self.assertIn(\"--session\", r.stderr)\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":5381,"content_sha256":"77a16deda2df742acb5c5405fb014584d270b686fc3d2e38e5e1d7e25eb3f3a7"},{"filename":"scripts/tests/test_render_encoding.py","content":"\"\"\"Native file rendering under a legacy Windows code page.\"\"\"\nimport json\nimport os\nimport re\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\nfrom pathlib import Path\n\nimport _paths\n\n\nclass NativeEncoding(unittest.TestCase):\n @unittest.skipUnless(os.name == \"nt\", \"Windows code-page regression\")\n def test_html_output_is_utf8_with_legacy_process_encoding(self):\n fixture = Path(_paths.FIXTURES) / \"report-empty.json\"\n data = json.loads(fixture.read_text(encoding=\"utf-8\"))\n # A valid empty single-session report has no period date labels.\n # This isolates file encoding from the separate Windows strftime defect.\n data[\"scope\"][\"kind\"] = \"session\"\n data[\"window\"][\"start_pt\"] = None\n data[\"generated_at_pt\"] = \"中文 report\"\n data[\"by_day\"] = []\n data[\"timeline\"] = {\"wins_by_day\": [], \"mistakes_by_day\": []}\n with tempfile.TemporaryDirectory() as folder:\n source = Path(folder) / \"report.json\"\n source.write_text(json.dumps(data, ensure_ascii=False), encoding=\"utf-8\")\n script = \"\"\"\nimport locale, pathlib, sys\nsys.path.insert(0, sys.argv[1])\nfrom acr import render\nencoding = locale.getpreferredencoding(False) # locale.getencoding() is Python 3.11+; the skill supports 3.9+\nassert encoding.lower() != \"utf-8\", encoding\nfor print_mode in (False, True):\n path = render.render_file(sys.argv[2], sys.argv[3], print_mode=print_mode)\n text = pathlib.Path(path).read_text(encoding=\"utf-8\")\n assert '\u003cmeta charset=\"utf-8\">' in text\n assert '‹' in text and '›' in text\n assert '中文 report' in text\n\"\"\"\n process = subprocess.run(\n [sys.executable, \"-X\", \"utf8=0\", \"-c\", script, _paths.SCRIPTS, str(source), folder],\n capture_output=True, env=dict(os.environ, PYTHONUTF8=\"0\"),\n )\n self.assertEqual(process.returncode, 0, process.stderr.decode(\"utf-8\", errors=\"replace\"))\n\n\nclass DateFormatting(unittest.TestCase):\n def test_day_label_uses_unpadded_day(self):\n from acr import render\n self.assertEqual(render.day_label(\"2026-10-02\"), \"Fri Oct 2\")\n self.assertEqual(render.day_label(\"2026-10-02\", short=True), \"Oct 2\")\n\n def test_date_pill_retains_single_day_and_range_labels(self):\n from acr import render\n scope = {\"kind\": \"period\"}\n cases = [\n (\"2026-10-02\", \"2026-10-03\", \"Oct 2, 2026 (PT)\"),\n (\"2026-10-02\", \"2026-10-04\", \"Oct 2 – 3, 2026 (PT)\"),\n (\"2026-09-29\", \"2026-10-03\", \"Sep 29 – Oct 2, 2026 (PT)\"),\n ]\n for start, end, expected in cases:\n with self.subTest(start=start, end=end):\n self.assertEqual(render.date_pill({\"start_pt\": start, \"end_exclusive_pt\": end}, scope), expected)\n\n def test_sources_use_no_platform_specific_strftime_flags(self):\n # \"%-d\" (no zero padding) is a glibc/BSD extension: Windows strftime raises\n # \"ValueError: Invalid format string\". Compose the day as f\"{d:%b} {d.day}\" instead.\n sources = [Path(_paths.ACR_PY), *sorted(Path(_paths.SCRIPTS, \"acr\").glob(\"*.py\"))]\n hits = [f\"{source.name}:{number}\" for source in sources\n for number, line in enumerate(source.read_text(encoding=\"utf-8\").splitlines(), 1)\n if re.search(r\"%-[A-Za-z]\", line)]\n self.assertEqual(hits, [])\n","content_type":"text/x-python; charset=utf-8","language":"python","size":3402,"content_sha256":"0143a3d34b86dd67c0c0af444258625798ebfe71d83494d12b0837245ef350a4"},{"filename":"scripts/tests/test_render.py","content":"\"\"\"Phase 3 tests (plan 3.6, 3.7, 3.8): the renderer against the five report fixtures.\"\"\"\nimport glob\nimport json\nimport os\nimport re\nimport shutil\nimport tempfile\nimport unittest\nfrom pathlib import Path\nfrom unittest.mock import patch\n\nimport _paths # noqa: F401\nfrom acr import render\n\nFIXTURES = sorted(glob.glob(os.path.join(_paths.FIXTURES, \"report-*.json\")))\nMONEY = re.compile(r\"\\$[0-9,]*\\.[0-9]*\")\nTWO_DEC = re.compile(r\"\\$[0-9,]*\\.[0-9][0-9]\")\n\n\ndef load(path):\n with open(path) as fh: return json.load(fh)\n\n\nclass Fixtures(unittest.TestCase):\n def setUp(self):\n self.assertEqual(len(FIXTURES), 5, FIXTURES)\n self.pages = {os.path.basename(f): render.page(load(f)) for f in FIXTURES}\n\n def test_no_script_or_external_resources(self):\n for name, h in self.pages.items():\n self.assertNotRegex(h, r\"\u003cscript|\u003clink|@import|url\\(http|src=\\\"http\", name)\n self.assertEqual(h.count(\"url(\"), h.count(\"url(#\"), name)\n\n def test_dollars_two_decimals_no_cent_sign(self):\n for name, h in self.pages.items():\n body = re.sub(r'\u003cspan class=\"ex\">.*?\u003c/span>', \"\", h)\n bad = [m for m in MONEY.findall(body) if not TWO_DEC.fullmatch(m)]\n self.assertEqual(bad, [], name); self.assertNotIn(chr(0xA2), h, name) # no cent sign anywhere\n self.assertIn(\"Measured provider spend: unavailable\", h, name)\n\n def test_layout_order_tiles_and_links(self):\n for name, h in self.pages.items():\n i_hero, i_wm, i_rib = h.find('class=\"hero'), h.find('class=\"wins-mistakes'), h.find('class=\"ribbon')\n self.assertTrue(0 \u003c= i_hero \u003c i_wm, name)\n self.assertTrue(i_rib == -1 or i_wm \u003c i_rib, name) # the ribbon is absent only when nothing is priced\n self.assertLessEqual(h.count('class=\"behavior-tile'), 4, name)\n self.assertEqual(h.count(\"Cost of mistakes\"), 1, name)\n for frag in set(re.findall(r'href=\"#((?:win|mistakes)-[^\"]+)\"', h)): self.assertIn(f'id=\"{frag}\"', h, f\"{name}: {frag}\")\n d = h.find('\u003cdetails id=\"details\"'); u = h.find(\"Upper bound\")\n self.assertTrue(u == -1 or u > d, name)\n\n def test_day_chart_and_empty_days(self):\n one, empty, thirty = self.pages[\"report-1day.json\"], self.pages[\"report-empty.json\"], self.pages[\"report-30day.json\"]\n self.assertEqual(one.count('class=\"cax\"'), 1 + 1) # one day column label + one timeline label\n self.assertIn(\"no agent work\", empty); self.assertIn(\"Nothing finished yet\", empty)\n self.assertIn(\"No merged PR or published version found\", empty)\n self.assertGreater(thirty.count(\"no agent work\"), 20)\n\n def test_every_money_figure_in_hero_has_a_tag(self):\n for name, h in self.pages.items():\n hero = h[h.find('class=\"hero'): h.find('class=\"wins-mistakes')]\n self.assertGreaterEqual(hero.count('class=\"est'), len(MONEY.findall(hero)) - 1, name) # the \"each\" figure shares the hero tag\n\n def test_deterministic(self):\n for f in FIXTURES: self.assertEqual(render.page(load(f)), render.page(load(f)))\n\n\nclass Files(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp()\n\n def tearDown(self):\n shutil.rmtree(self.tmp)\n\n def test_render_file_and_print_mode(self):\n p = render.render_file(FIXTURES[0], self.tmp)\n self.assertTrue(p.endswith(\"report.html\")); self.assertIn('\u003cdetails id=\"details\" >', open(p).read())\n q = render.render_file(FIXTURES[0], self.tmp, print_mode=True)\n self.assertTrue(q.endswith(\"report.print.html\")); self.assertIn('\u003cdetails id=\"details\" open>', open(q).read())\n\n def test_failed_render_keeps_the_previous_report(self):\n p = render.render_file(FIXTURES[0], self.tmp)\n with open(p, \"rb\") as fh: before = fh.read()\n self.assertIn(b\"\u003c!doctype html>\", before)\n d = load(FIXTURES[0]); del d[\"line_items\"] # malformed report.json: page() raises KeyError\n bad = os.path.join(self.tmp, \"broken.json\")\n with open(bad, \"w\", encoding=\"utf-8\") as fh: json.dump(d, fh)\n with self.assertRaises(KeyError): render.render_file(bad, self.tmp)\n with open(p, \"rb\") as fh: self.assertEqual(fh.read(), before) # not truncated to 0 bytes (#4250)\n\n def test_pdf_skipped_without_chrome(self):\n render.render_file(FIXTURES[0], self.tmp, print_mode=True)\n path, msg = render.pdf(self.tmp, chrome=\"no-such-browser-xyz\")\n self.assertIsNone(path); self.assertIn(\"PDF skipped, HTML is canonical\", msg)\n\n def test_usd2_is_the_only_formatter(self):\n src = open(os.path.join(_paths.SCRIPTS, \"acr\", \"render.py\")).read()\n self.assertNotIn(\"def \" + \"cen\" + \"ts\", src); self.assertNotIn(chr(0xA2), src); self.assertEqual(render.usd2(1234.5), \"$1,234.50\")\n\n\ndef posix(path):\n return os.fspath(path).replace(\"\\\\\", \"/\")\n\n\nMAC_APP = \"Google Chrome.app/Contents/MacOS/Google Chrome\"\nPDF = b\"%PDF-1.4\\n1 0 obj\\n\u003c\u003c>>\\nendobj\\ntrailer\\n\u003c\u003c>>\\n%%EOF\\n\"\n\n\nclass FakeChrome:\n \"\"\"subprocess.Popen stand-in: prints `pdf` to the --print-to-pdf= path (nothing when None), writes `stderr`, then\n exits with `exit_code`, or, like Chrome on macOS, keeps running until it is terminated (`exits=False`).\"\"\"\n def __init__(self, pdf=PDF, stderr=b\"\", exit_code=0, exits=True):\n self.pdf, self.stderr, self.exit_code, self.exits = pdf, stderr, exit_code, exits\n self.cmds, self.terminated, self.returncode = [], False, None\n\n def __call__(self, cmd, stdout=None, stderr=None):\n self.cmds.append(cmd); self.returncode = None\n if self.pdf is not None:\n with open(next(a for a in cmd if a.startswith(\"--print-to-pdf=\"))[len(\"--print-to-pdf=\"):], \"wb\") as fh: fh.write(self.pdf)\n stderr.write(self.stderr)\n return self\n\n def poll(self):\n if self.returncode is None and self.exits: self.returncode = self.exit_code\n return self.returncode\n\n def wait(self, timeout=None):\n return self.poll()\n\n def terminate(self):\n self.terminated, self.returncode = True, -15\n\n kill = terminate\n\n\nclass ChromeLookup(unittest.TestCase):\n \"\"\"pdf() on every OS with sys.platform, PATH, the install check and the Chrome process patched: the default\n lookup per platform, and how a Chrome that exits, fails, hangs or keeps running after printing is handled.\"\"\"\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr pdf #\") # a space and a '#' must survive into the file URL\n self.addCleanup(shutil.rmtree, self.tmp, True)\n with open(os.path.join(self.tmp, \"report.print.html\"), \"w\", encoding=\"utf-8\") as fh: fh.write(\"\u003cp>report\u003c/p>\")\n\n def pdf(self, platform, on_path=(), installed=(), env=None, chrome=\"google-chrome\", fake=None):\n fake = fake or FakeChrome()\n with patch(\"sys.platform\", platform), patch(\"shutil.which\", lambda name: \"/usr/bin/\" + name if name in on_path else None), \\\n patch(\"os.path.isfile\", lambda p: posix(p) in installed), patch.dict(os.environ, env or {}), patch(\"subprocess.Popen\", fake), \\\n patch.object(render, \"CHROME_EXIT_GRACE_S\", 0.05), patch.object(render, \"CHROME_TIMEOUT_S\", 0.3):\n path, msg = render.pdf(self.tmp, chrome=chrome)\n return path, msg, [posix(cmd[0]) for cmd in fake.cmds], fake\n\n def mac(self, **kw):\n return self.pdf(\"darwin\", installed=(\"/Applications/\" + MAC_APP,), **kw)\n\n def test_linux_default_is_google_chrome_on_path(self):\n self.assertEqual(self.pdf(\"linux\", on_path=(\"google-chrome\",))[2], [\"/usr/bin/google-chrome\"])\n path, msg, exes, _ = self.pdf(\"linux\", installed=(\"/Applications/\" + MAC_APP,))\n self.assertEqual((path, exes), (None, [])); self.assertIn(\"PDF skipped, HTML is canonical\", msg)\n\n def test_macos_default_finds_the_app_bundle_off_path(self):\n self.assertEqual(self.mac()[2], [\"/Applications/\" + MAC_APP])\n user_app = posix(os.path.join(os.path.expanduser(\"~\"), \"Applications\", *MAC_APP.split(\"/\")))\n self.assertEqual(self.pdf(\"darwin\", installed=(user_app,))[2], [user_app])\n self.assertEqual(self.pdf(\"darwin\", on_path=(\"google-chrome\",), installed=(\"/Applications/\" + MAC_APP,))[2], [\"/usr/bin/google-chrome\"])\n\n def test_windows_default_finds_installed_chrome_off_path(self):\n env = {\"PROGRAMFILES\": r\"C:\\Program Files\", \"PROGRAMFILES(X86)\": r\"C:\\Program Files (x86)\", \"LOCALAPPDATA\": r\"C:\\Users\\u\\AppData\\Local\"}\n local = \"C:/Users/u/AppData/Local/Google/Chrome/Application/chrome.exe\"\n self.assertEqual(self.pdf(\"win32\", installed=(local,), env=env)[2], [local])\n self.assertEqual(self.pdf(\"win32\", on_path=(\"chrome\",), installed=(local,), env=env)[2], [\"/usr/bin/chrome\"])\n\n def test_explicit_chrome_is_never_substituted(self):\n path, msg, exes, _ = self.mac(chrome=\"no-such-browser-xyz\")\n self.assertEqual((path, exes), (None, [])); self.assertIn(\"PDF skipped\", msg)\n\n def test_chrome_gets_a_file_uri(self):\n path, msg, _, fake = self.mac()\n self.assertEqual(msg, f\"pdf: {path}\")\n url = fake.cmds[0][-1]\n self.assertEqual(url, Path(self.tmp, \"report.print.html\").resolve().as_uri())\n self.assertIn(\"%23\", url); self.assertNotIn(\" \", url) # a raw '#' would start a URL fragment\n\n def test_chrome_that_keeps_running_after_printing_is_stopped(self):\n path, msg, _, fake = self.mac(fake=FakeChrome(exits=False)) # Chrome on macOS writes the PDF, then never exits\n self.assertEqual((path, msg, fake.terminated), (os.path.join(self.tmp, \"report.pdf\"), f\"pdf: {path}\", True))\n with open(path, \"rb\") as fh: self.assertEqual(fh.read(), PDF)\n self.assertFalse(os.path.exists(os.path.join(self.tmp, \".chrome-profile\")))\n\n def test_failed_print_reports_stderr_and_keeps_the_last_pdf(self):\n with open(os.path.join(self.tmp, \"report.pdf\"), \"wb\") as fh: fh.write(PDF)\n path, msg, _, _ = self.mac(fake=FakeChrome(pdf=b\"%PDF-1.4 cut short\", stderr=b\"\\xff\\xfe bad flag \\xe2\\x80\\x94 exiting\", exit_code=1))\n self.assertIsNone(path); self.assertIn(\"PDF skipped, HTML is canonical (chrome exit 1:\", msg); self.assertIn(\"bad flag — exiting\", msg)\n with open(os.path.join(self.tmp, \"report.pdf\"), \"rb\") as fh: self.assertEqual(fh.read(), PDF) # not replaced by a partial print\n\n def test_chrome_that_never_prints_times_out(self):\n path, msg, _, fake = self.mac(fake=FakeChrome(pdf=None, exits=False))\n self.assertEqual((path, msg, fake.terminated), (None, \"PDF skipped, HTML is canonical (chrome timed out)\", True))\n\n\nclass Excerpts(unittest.TestCase):\n def test_top_example_episode_is_listed_once(self):\n d = load(os.path.join(_paths.FIXTURES, \"report-1day.json\")); day = d[\"timeline\"][\"mistakes_by_day\"][0]\n ep = dict(episode_id=\"EP-1\", session=\"cs-ep\", ts_pt=\"2026-09-18 10:00 PT\", day_pt=day[\"day_pt\"], pattern=\"P12_unclear\", messages_n=1, low_usd=0.2, high_usd=0.3,\n cost_status=\"measured_tokens\", alex_minutes=1.0, model=None, model_status=\"assumed\", source=\"claude-code\", device=\"local\", excerpt=\"UNIQUE-EXCERPT-MARKER\", wasted_keys=[])\n d[\"behavior\"][\"episodes\"] = [ep]\n day[\"top_examples\"] = [dict(mistake_id=\"MK-x\", episode_id=ep[\"episode_id\"], pattern=ep[\"pattern\"], content_session_id=ep[\"session\"], ts_pt=ep[\"ts_pt\"])]\n h = render.page(d)\n self.assertEqual(h.count(\"UNIQUE-EXCERPT-MARKER\"), 1); self.assertIn(ep[\"cost_status\"], h)\n\n\nclass Unmeasured(unittest.TestCase):\n def test_no_ratio_never_prints_zero_extrapolated(self):\n li = dict(has_transcript=False, cost_extrapolated=None, attributed_usd=0.0, cost_basis=\"extrapolated\")\n self.assertEqual((render._li_money(li), render._li_basis(li)), (\"unmeasured\", \"unmeasured (no ratio)\"))\n li = dict(has_transcript=False, cost_extrapolated=1.5, attributed_usd=1.5, cost_basis=\"extrapolated\")\n self.assertEqual((render._li_money(li), render._li_basis(li)), (\"$1.50\", \"extrapolated\"))\n d = load(FIXTURES[0]); d[\"spend\"][\"extrapolated_unmeasured_usd\"] = None; d[\"spend\"][\"extrapolation_basis\"] = \"n/a (no measured session to derive a ratio from)\"\n for li in d[\"line_items\"]:\n if not li.get(\"has_transcript\"): li[\"cost_extrapolated\"] = None; li[\"attributed_usd\"] = 0.0\n h = render.page(d)\n self.assertIn(\"unavailable (no measured session to derive a ratio from)\", h); self.assertNotIn(\"$0.00 \u003cspan class=\\\"est extra\\\">\", h)\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":12664,"content_sha256":"a66bdbb07c21a77e7097cace0529bec6d2c1bda050bdec54e192074178ba0c5f"},{"filename":"scripts/tests/test_rollup.py","content":"import copy\nimport datetime as dt\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\n\nimport _paths\nimport _db\nfrom acr import costs, evidence, labels, period, rollup\nfrom acr.period import PT\n\nPRICES = json.load(open(os.path.join(_paths.FIXTURES, \"prices_sample.json\")))\nPRICES[\"models\"][\"test/no-output\"] = dict(input=1.0, output=None, cache_read=0.1, cache_write=1.25, cache_write_1h=None)\nPRICES[\"models\"][\"test/negative\"] = dict(input=-1.0, output=-1.0, cache_read=-1.0, cache_write=-1.0, cache_write_1h=None)\nPRICES[\"models\"][\"google/gemini-2.5-flash-lite\"] = dict(input=0.1, output=0.4, cache_read=None, cache_write=None, cache_write_1h=None)\nNOW = dt.datetime(2026, 9, 25, 12, 0, tzinfo=PT)\nW = period.resolve_window(\"2026-09-18\", \"2026-09-21\", now=NOW)\nS = W.start_epoch_ms\nH = 3600 * 1000\nREMOTE_UUID = \"45b12c6b-d8b8-49ad-bb7f-ef49a85bab44\"\nCS4 = dict(inp=100000, out=10000) # big enough that the abandoned session's waste is not $0.00\n\n\ndef ms(day_offset, hours):\n return S + day_offset * 24 * H + int(hours * H)\n\n\ndef ts(m):\n return dt.datetime.fromtimestamp(m / 1000, PT).isoformat()\n\n\ndef row(session, m, model=\"claude-fable-5-1\", inp=1000, out=100, cw5=0, cw1h=0, cr=0, file=\"/t/a.jsonl\", cwd=\"/w/p\"):\n return dict(src=\"claude-code\", file=file, dir=\"-w-p\", cwd=cwd, session=session, sidechain=False, model=model, ts=ts(m), input=inp, output=out,\n cache_write=cw5 + cw1h, cache_write_1h=cw1h, cache_write_5m=cw5, cache_read=cr, cost_usd_reported=None, device=\"local\")\n\n\ndef fable_usd(inp=1000, out=100, cw5=0, cw1h=0, cr=0):\n return (inp * 10 + out * 50 + cw5 * 12.5 + cw1h * 20 + cr * 0.25) / 1e6\n\n\nclass Fixture(unittest.TestCase):\n \"\"\"S1: local session with a transcript, completed summary, ship observation, triplicated observer rows.\n S2: remote replica (no transcript), completed. S3: trivial. S4: abandoned local with a recovery prompt.\"\"\"\n\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr-rollup-\"); self.dbp = os.path.join(self.tmp, \"claude-mem.db\")\n db = _db.make_db(self.dbp)\n _db.add_session(db, \"cs1\", \"m1\", \"claude-mem\", ms(0, 9), completed_ms=ms(0, 10), user_prompt=\"implement the rollup\")\n for _ in range(3): _db.add_obs(db, \"m1\", \"claude-mem\", \"discovery\", \"read files\", 5000, ms(0, 9.2)) # same per-turn value x3\n self.ship_id = _db.add_obs(db, \"m1\", \"claude-mem\", \"change\", \"PR #4125 merged to main\", 7000, ms(0, 9.5))\n _db.add_summary(db, \"m1\", \"claude-mem\", \"implement the rollup\", \"Rollup implemented and merged\", ms(0, 9.9))\n _db.add_prompt(db, \"cs1\", 1, \"implement the rollup\", ms(0, 9))\n _db.add_tool(db, \"cs1\", \"claude-mem\", ms(0, 9.1))\n _db.add_session(db, \"cs2\", \"m2\", \"claude-mem/wt\", ms(1, 8), completed_ms=ms(1, 9), user_prompt=\"fix the bug\")\n _db.add_obs(db, \"m2\", \"claude-mem/wt\", \"bugfix\", \"fixed it\", 20000, ms(1, 8.5), device=REMOTE_UUID)\n _db.add_summary(db, \"m2\", \"claude-mem/wt\", \"fix the bug\", \"Fixed the bug in the sync path\", ms(1, 8.9), device=REMOTE_UUID)\n _db.add_session(db, \"cs3\", \"m3\", \"helper\", ms(0, 20), completed_ms=ms(0, 20))\n _db.add_session(db, \"cs4\", \"m4\", \"other\", ms(1, 12), completed_ms=ms(1, 13), user_prompt=\"resume the dig after the usage-limit reset\")\n _db.add_obs(db, \"m4\", \"other\", \"discovery\", \"poked around\", 1000, ms(1, 12.5))\n _db.add_prompt(db, \"cs4\", 1, \"resume the dig after the usage-limit reset\", ms(1, 12))\n db.commit(); db.close()\n self.rows = [row(\"cs1\", ms(0, 9.3)), row(\"cs1\", ms(0, 9.6), cw5=2000, cw1h=1000, cr=50000),\n row(\"cs4\", ms(1, 12.4), **CS4), row(\"orphan\", ms(2, 5), inp=100, out=10, file=\"/t/o.jsonl\")]\n\n def tearDown(self):\n shutil.rmtree(self.tmp, ignore_errors=True)\n\n def build(self, rows=None, scope=None, prices=None, behavior=False): # Phase 2 tests exercise the draft waste split; Phase 2B has its own tests\n import sqlite3\n db = sqlite3.connect(self.dbp); db.row_factory = sqlite3.Row\n try:\n usage = dict(window=W.block(), rows=copy.deepcopy(rows if rows is not None else self.rows))\n return rollup.build(usage, prices or PRICES, db, scope or evidence.Scope(\"period\", S=W.start_epoch_ms, E=W.end_epoch_ms), W.block(),\n now=NOW, use_gh=False, remote=lambda cwd: None, behavior=behavior)\n finally:\n db.close()\n\n\nclass Rollup(Fixture):\n def test_observer_dedup_counts_triplicated_value_once(self):\n r, ev, _ = self.build()\n e1 = next(x for x in ev if x[\"memory_session_id\"] == \"m1\")\n self.assertEqual(e1[\"observer_tokens_raw\"], 3 * 5000 + 7000)\n self.assertEqual(e1[\"observer_tokens\"], 5000 + 7000)\n self.assertAlmostEqual(r[\"spend\"][\"observer_note_taker_est_usd\"], round((12000 + 20000 + 1000) * 0.1 / 1e6, 2))\n\n def test_observer_never_changes_agent_estimate_or_by_day(self):\n base, _, _ = self.build()\n expected = fable_usd() + fable_usd(cw5=2000, cw1h=1000, cr=50000) + fable_usd(**CS4) + fable_usd(100, 10)\n self.assertAlmostEqual(base[\"spend\"][\"agent_estimated_usd\"], round(expected, 2))\n self.assertEqual(base[\"spend\"][\"headline_usd\"], base[\"spend\"][\"agent_estimated_usd\"])\n import sqlite3\n db = sqlite3.connect(self.dbp); db.execute(\"update observations set discovery_tokens = discovery_tokens * 100\"); db.commit(); db.close()\n big, _, _ = self.build()\n self.assertEqual(big[\"spend\"][\"agent_estimated_usd\"], base[\"spend\"][\"agent_estimated_usd\"])\n self.assertEqual([d[\"agent_estimated_usd\"] for d in big[\"by_day\"]], [d[\"agent_estimated_usd\"] for d in base[\"by_day\"]])\n self.assertEqual([c[\"agent_estimated_usd\"] for c in big[\"by_category\"]], [c[\"agent_estimated_usd\"] for c in base[\"by_category\"]])\n self.assertGreater(big[\"spend\"][\"observer_note_taker_est_usd\"], base[\"spend\"][\"observer_note_taker_est_usd\"])\n\n def test_measured_is_null_with_status_never_zero(self):\n r, _, _ = self.build()\n self.assertIsNone(r[\"spend\"][\"agent_measured_usd\"]); self.assertEqual(r[\"spend\"][\"measured_status\"], \"unavailable\")\n self.assertEqual(r[\"spend\"][\"grok_bot_usage\"][\"status\"], \"unavailable\"); self.assertNotIn(\"seat\", str(r[\"spend\"][\"grok_bot_usage\"].get(\"status\")))\n self.assertTrue(all(li[\"cost_measured\"] == \"unavailable\" for li in r[\"line_items\"]))\n\n def test_by_day_covers_every_day_including_empty(self):\n r, _, _ = self.build()\n self.assertEqual([d[\"day_pt\"] for d in r[\"by_day\"]], [\"2026-09-18\", \"2026-09-19\", \"2026-09-20\"])\n d3 = r[\"by_day\"][2]\n self.assertFalse(d3[\"no_agent_work\"]) # the orphan transcript row is on day 3\n r2, _, _ = self.build(rows=self.rows[:3])\n self.assertTrue(r2[\"by_day\"][2][\"no_agent_work\"]); self.assertEqual(r2[\"by_day\"][2][\"agent_estimated_usd\"], 0)\n self.assertEqual(len(r2[\"timeline\"][\"wins_by_day\"]), 3)\n\n def test_extrapolation_only_touches_sessions_without_transcript(self):\n r, _, _ = self.build()\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertEqual(li[\"m1\"][\"cost_basis\"], \"estimated_usage\"); self.assertIsNone(li[\"m1\"][\"cost_extrapolated\"])\n self.assertEqual(li[\"m2\"][\"cost_basis\"], \"extrapolated\"); self.assertIsNone(li[\"m2\"][\"cost_estimated\"])\n measured_usd = fable_usd() + fable_usd(cw5=2000, cw1h=1000, cr=50000) + fable_usd(**CS4)\n ratio = measured_usd / (12000 + 1000) # USD per observer token over transcript sessions\n self.assertAlmostEqual(li[\"m2\"][\"cost_extrapolated\"], round(20000 * ratio, 2), places=2)\n self.assertAlmostEqual(r[\"spend\"][\"extrapolated_unmeasured_usd\"], round(20000 * ratio, 2), places=2)\n self.assertTrue(r[\"spend\"][\"extrapolation_basis\"].startswith(\"EXTRAPOLATED (low confidence)\"))\n self.assertAlmostEqual(r[\"spend\"][\"total_estimate_usd\"], round(r[\"spend\"][\"agent_estimated_usd\"] + r[\"spend\"][\"extrapolated_unmeasured_usd\"], 2))\n\n def test_labels_start_keyword_and_only_review_changes_them(self):\n r, _, review = self.build()\n self.assertTrue(all(li[\"label_source\"] == \"keyword\" for li in r[\"line_items\"]))\n self.assertEqual(r[\"labels\"], dict(reviewed=0, total=5))\n self.assertTrue(all(en[\"label_source\"] == \"keyword\" and en[\"draft_category\"] for en in review[\"items\"]))\n rollup.aggregate(r) # aggregation alone never promotes a label\n self.assertEqual(r[\"labels\"][\"reviewed\"], 0)\n n = labels.apply_review(r[\"line_items\"], dict(items=[dict(work_item_id=\"WI-1\", category=\"Feature\", failure_type=\"Rework\", reviewed_by=\"Alex\"),\n dict(work_item_id=\"WI-2\", category=\"Bug fix\", reviewed_by=\"anthropic/claude-x\")]), \"2026-09-25 12:00 PT\")\n rollup.aggregate(r)\n self.assertEqual(n, 2); self.assertEqual(r[\"labels\"], dict(reviewed=2, total=5))\n li = {x[\"work_item_id\"]: x for x in r[\"line_items\"]}\n self.assertEqual((li[\"WI-1\"][\"label_source\"], li[\"WI-1\"][\"category\"], li[\"WI-1\"][\"failure_type\"]), (\"human\", \"Feature\", \"Rework\"))\n self.assertEqual(li[\"WI-2\"][\"label_source\"], \"llm\"); self.assertEqual(li[\"WI-3\"][\"label_source\"], \"keyword\")\n self.assertIn(\"Rework\", [f[\"failure_type\"] for f in r[\"failure_economics\"]])\n\n def test_unpriced_models_contribute_zero_with_flag(self):\n rows = self.rows + [row(\"cs1\", ms(0, 9.7), model=\"test/no-output\"), row(\"cs1\", ms(0, 9.8), model=\"test/negative\"), row(\"cs1\", ms(0, 9.85), model=\"\u003csynthetic>\")]\n base, _, _ = self.build(); r, _, _ = self.build(rows=rows)\n self.assertEqual(r[\"spend\"][\"agent_estimated_usd\"], base[\"spend\"][\"agent_estimated_usd\"])\n self.assertEqual({(u[\"model\"], u[\"reason\"]) for u in r[\"unpriced_models\"]},\n {(\"test/no-output\", \"no output price listed\"), (\"test/negative\", \"negative (variable) price listed\"), (\"\u003csynthetic>\", \"not in price list\")})\n self.assertEqual(r[\"totals\"][\"tokens\"][\"api_calls\"], len(rows))\n self.assertTrue(any(a[\"kind\"] == \"unpriced\" for a in r[\"attention\"]))\n self.assertEqual(sum(m[\"unpriced_calls\"] for m in r[\"by_model\"]), 3)\n\n def test_join_key_and_unmatched_still_counted(self):\n r, _, _ = self.build()\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertTrue(li[\"m1\"][\"has_transcript\"]); self.assertEqual(li[\"m1\"][\"agent_calls\"], 2)\n self.assertEqual(r[\"unmatched_transcripts\"][\"count\"], 1); self.assertEqual(r[\"unmatched_transcripts\"][\"sessions\"][0][\"session\"], \"orphan\")\n self.assertAlmostEqual(r[\"unmatched_transcripts\"][\"usd\"], round(fable_usd(100, 10), 2))\n self.assertEqual(r[\"totals\"][\"tokens\"][\"input\"], 1000 + 1000 + CS4[\"inp\"] + 100)\n\n def test_devices_labeled_never_uuid(self):\n r, ev, review = self.build()\n self.assertEqual([d[\"device\"] for d in r[\"devices\"]], [\"local\", \"remote-1\"])\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertEqual(li[\"m2\"][\"device\"], [\"remote-1\"]); self.assertEqual(li[\"m1\"][\"device\"], [\"local\"])\n self.assertNotIn(REMOTE_UUID, json.dumps([r, ev, review])); self.assertEqual(r[\"totals\"][\"devices\"], 2)\n\n def test_statuses_trivial_and_totals(self):\n r, _, _ = self.build()\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertEqual(li[\"m1\"][\"status\"], \"shipped\"); self.assertEqual(li[\"m2\"][\"status\"], \"completed\")\n self.assertEqual(li[\"m3\"][\"status\"], \"abandoned\"); self.assertTrue(li[\"m3\"][\"trivial\"]); self.assertEqual(li[\"m4\"][\"status\"], \"abandoned\")\n self.assertEqual(li[\"m4\"][\"failure_signals\"], [\"Recovery after miss\"]); self.assertEqual(li[\"m4\"][\"failure_type\"], \"Recovery after miss\")\n t = r[\"totals\"]\n self.assertEqual((t[\"sessions\"], t[\"real_work_sessions\"], t[\"finished_outcomes\"], t[\"ship_events\"], t[\"projects\"], t[\"repos\"]), (4, 3, 2, 1, 4, 3)); self.assertEqual(t[\"transcript_only_sessions\"], 1) # the orphan transcript is its own line item, counted apart\n orphan = next(li for li in r[\"line_items\"] if li[\"content_session_id\"] == \"orphan\")\n self.assertEqual((orphan[\"status\"], orphan[\"cost_basis\"], orphan[\"has_transcript\"], orphan[\"confidence\"]), (\"in_progress\", \"estimated_usage\", True, \"low\"))\n self.assertEqual(li[\"m4\"][\"wasted_cost\"], li[\"m4\"][\"attributed_usd\"]); self.assertGreater(t[\"waste_rate\"], 0)\n self.assertEqual(r[\"trivial_sessions\"][0][\"work_item_id\"], li[\"m3\"][\"work_item_id\"])\n self.assertIsInstance(r[\"failure_economics\"], list); self.assertLessEqual(len(r[\"attention\"]), 3)\n self.assertNotIn(\"Rework\", [c[\"category\"] for c in r[\"by_category\"]])\n\n def test_session_scope_on_transcript_only_id_builds(self):\n r, _, _ = self.build(scope=evidence.Scope(\"session\", session=\"orphan\")) # no claude-mem session: n must still be bound\n self.assertEqual([li[\"work_item_id\"] for li in r[\"line_items\"]], [\"WI-1\"]); self.assertTrue(r[\"line_items\"][0][\"orphan\"])\n self.assertEqual(r[\"unmatched_transcripts\"][\"count\"], 1)\n\n def test_usage_rows_outside_window_are_not_priced(self):\n r, _, _ = self.build(rows=self.rows + [row(\"cs1\", ms(-1, 5), inp=999999), row(\"cs1\", ms(3, 0), inp=999999)]) # a wider shared collect\n self.assertEqual(r[\"window\"][\"usage_rows_outside_window\"], 2)\n self.assertEqual(r[\"totals\"][\"tokens\"][\"input\"], 1000 + 1000 + CS4[\"inp\"] + 100)\n self.assertEqual(r[\"spend\"][\"agent_estimated_usd\"], self.build()[0][\"spend\"][\"agent_estimated_usd\"])\n\n def test_no_ratio_marks_sessions_without_transcript_unmeasured(self):\n r, _, _ = self.build(rows=[]) # nothing measured on this box: no ratio\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertEqual((li[\"m2\"][\"cost_status\"], li[\"m2\"][\"cost_extrapolated\"], li[\"m2\"][\"attributed_usd\"]), (\"unmeasured\", None, 0.0))\n self.assertIsNone(r[\"spend\"][\"extrapolated_unmeasured_usd\"]); self.assertTrue(r[\"spend\"][\"extrapolation_basis\"].startswith(\"n/a\"))\n r, _, _ = self.build()\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}\n self.assertEqual((li[\"m1\"][\"cost_status\"], li[\"m2\"][\"cost_status\"]), (\"estimated\", \"extrapolated\"))\n\n def test_project_scope_metadata_excludes_other_projects(self):\n r, _, _ = self.build(scope=evidence.Scope(\"project\", S=W.start_epoch_ms, E=W.end_epoch_ms, project=\"claude-mem\"))\n self.assertEqual(r[\"observer\"][\"rows_in_scope\"], dict(observations=5, summaries=2, prompts=1, tool_uses=1)) # m4's \"other\" project rows stay out\n self.assertEqual(r[\"totals\"][\"devices\"], 2)\n\n def test_review_recomputes_per_day_finished_outcomes(self):\n r, _, _ = self.build()\n self.assertEqual(sum(d[\"finished_outcomes\"] for d in r[\"by_day\"]), r[\"totals\"][\"finished_outcomes\"])\n li = {x[\"session_ids\"][0]: x for x in r[\"line_items\"]}; self.assertEqual(li[\"m4\"][\"status\"], \"abandoned\")\n labels.apply_review(r[\"line_items\"], [dict(work_item_id=li[\"m4\"][\"work_item_id\"], category=\"Investigation\", reviewed_by=\"Alex\", status=\"completed\")], \"t\")\n rollup.aggregate(r)\n self.assertEqual(r[\"totals\"][\"finished_outcomes\"], 3); self.assertEqual(sum(d[\"finished_outcomes\"] for d in r[\"by_day\"]), 3)\n self.assertEqual(next(d for d in r[\"by_day\"] if d[\"day_pt\"] == li[\"m4\"][\"date_pt\"])[\"finished_outcomes\"], 2)\n orphan = next(x for x in r[\"line_items\"] if x.get(\"orphan\")) # a reviewed transcript-only item stays out of both the headline and the days\n labels.apply_review(r[\"line_items\"], [dict(work_item_id=orphan[\"work_item_id\"], category=\"Feature\", reviewed_by=\"Alex\", status=\"shipped\")], \"t\")\n rollup.aggregate(r)\n self.assertEqual((r[\"totals\"][\"finished_outcomes\"], sum(d[\"finished_outcomes\"] for d in r[\"by_day\"])), (3, 3))\n\n def test_project_scope_includes_worktrees_and_session_scope(self):\n r, _, _ = self.build(scope=evidence.Scope(\"project\", S=W.start_epoch_ms, E=W.end_epoch_ms, project=\"claude-mem\"))\n self.assertEqual({x[\"session_ids\"][0] for x in r[\"line_items\"]}, {\"m1\", \"m2\"}); self.assertEqual(r[\"unmatched_transcripts\"][\"count\"], 0)\n r, _, _ = self.build(scope=evidence.Scope(\"session\", session=\"cs1\"))\n self.assertEqual(len(r[\"line_items\"]), 1); self.assertEqual([d[\"day_pt\"] for d in r[\"by_day\"]], [\"2026-09-18\"])\n # a shared period usage.json: only cs1's two rows are priced, not cs4's or the orphan's\n self.assertEqual(r[\"spend\"][\"agent_estimated_usd\"], round(fable_usd() + fable_usd(cw5=2000, cw1h=1000, cr=50000), 2))\n self.assertEqual(r[\"totals\"][\"tokens\"][\"api_calls\"], 2); self.assertEqual(r[\"unmatched_transcripts\"][\"count\"], 0)\n\n\nclass Cli(Fixture):\n def test_rollup_and_review_cli(self):\n out = os.path.join(self.tmp, \"out\"); os.makedirs(out)\n json.dump(dict(window=W.block(), collector={}, rows=self.rows), open(os.path.join(out, \"usage.json\"), \"w\"))\n pf = os.path.join(self.tmp, \"prices.json\"); json.dump(PRICES, open(pf, \"w\"))\n r = subprocess.run([sys.executable, _paths.ACR_PY, \"rollup\", \"--start\", \"2026-09-18\", \"--end\", \"2026-09-21\", \"--out\", out, \"--prices\", pf,\n \"--db\", self.dbp, \"--no-gh\"], capture_output=True, text=True)\n self.assertEqual(r.returncode, 0, r.stderr)\n for f in (\"report.json\", \"line-items.csv\", \"evidence.json\", \"labels.review.json\", \"snapshot.db\"):\n self.assertTrue(os.path.exists(os.path.join(out, f)), f)\n rep = json.load(open(os.path.join(out, \"report.json\")))\n self.assertEqual(rep[\"spend\"][\"measured_status\"], \"unavailable\"); self.assertEqual(rep[\"labels\"][\"reviewed\"], 0)\n rv = json.load(open(os.path.join(out, \"labels.review.json\")))\n rv[\"items\"][0].update(category=\"Feature\", reviewed_by=\"Alex\")\n rf = os.path.join(self.tmp, \"reviewed.json\"); json.dump(rv, open(rf, \"w\"))\n r = subprocess.run([sys.executable, _paths.ACR_PY, \"review\", \"--apply\", rf, \"--out\", out], capture_output=True, text=True)\n self.assertEqual(r.returncode, 0, r.stderr)\n rep = json.load(open(os.path.join(out, \"report.json\")))\n self.assertEqual(rep[\"labels\"], dict(reviewed=1, total=5)); self.assertEqual(rep[\"line_items\"][0][\"label_source\"], \"human\")\n rv[\"items\"][1].update(category=\"Feature\", failure_type=\"Made-up type\", reviewed_by=\"Alex\"); json.dump(rv, open(rf, \"w\"))\n r = subprocess.run([sys.executable, _paths.ACR_PY, \"review\", \"--apply\", rf, \"--out\", out], capture_output=True, text=True)\n self.assertNotEqual(r.returncode, 0); self.assertIn(\"unknown failure type\", r.stderr)\n\n def test_rollup_rejects_session_with_period(self):\n r = subprocess.run([sys.executable, _paths.ACR_PY, \"rollup\", \"--session\", \"x\", \"--start\", \"2026-09-18\", \"--out\", self.tmp], capture_output=True, text=True)\n self.assertNotEqual(r.returncode, 0); self.assertIn(\"--session\", r.stderr)\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":19245,"content_sha256":"9ca3e43df448955189e686eac7229c6e5de59458fb30278b3d923ca160c67b6d"},{"filename":"scripts/tests/test_snapshot.py","content":"import os\nimport shutil\nimport sqlite3\nimport tempfile\nimport unittest\n\nimport _paths # noqa: F401\nfrom acr import snapshot\n\n\nclass DbPath(unittest.TestCase):\n def test_env_override(self):\n self.assertEqual(snapshot.db_path({\"CLAUDE_MEM_DATA_DIR\": \"/data/mem\"}), \"/data/mem/claude-mem.db\")\n\n def test_default_is_home_claude_mem(self):\n self.assertEqual(snapshot.db_path({}), os.path.expanduser(\"~/.claude-mem/claude-mem.db\"))\n self.assertEqual(snapshot.db_path({\"CLAUDE_MEM_DATA_DIR\": \"\"}), os.path.expanduser(\"~/.claude-mem/claude-mem.db\"))\n\n\nclass Backup(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr-snap-\")\n self.live = os.path.join(self.tmp, \"live\", \"claude-mem.db\")\n os.makedirs(os.path.dirname(self.live))\n db = sqlite3.connect(self.live)\n db.execute(\"PRAGMA journal_mode=WAL\")\n db.execute(\"CREATE TABLE sdk_sessions (content_session_id TEXT, project TEXT, started_at_epoch INTEGER)\")\n db.executemany(\"INSERT INTO sdk_sessions VALUES (?,?,?)\", [(\"s1\", \"p\", 1), (\"s2\", \"q\", 2)])\n db.commit()\n db.close()\n\n def tearDown(self):\n shutil.rmtree(self.tmp, ignore_errors=True)\n\n def test_snapshot_is_a_faithful_copy_and_live_is_untouched(self):\n before = os.stat(self.live)\n out = os.path.join(self.tmp, \"out\")\n snap = snapshot.snapshot(out, live=self.live)\n self.assertEqual(snap, os.path.join(out, \"snapshot.db\"))\n after = os.stat(self.live)\n self.assertEqual((before.st_mtime_ns, before.st_size), (after.st_mtime_ns, after.st_size))\n db = snapshot.open_snapshot(snap)\n rows = db.execute(\"SELECT content_session_id, project FROM sdk_sessions ORDER BY 1\").fetchall()\n db.close()\n self.assertEqual([tuple(r) for r in rows], [(\"s1\", \"p\"), (\"s2\", \"q\")])\n # a second run replaces the stale snapshot\n snap2 = snapshot.snapshot(out, live=self.live)\n self.assertEqual(snap, snap2)\n\n def test_env_resolution_used_when_live_not_given(self):\n os.environ[\"CLAUDE_MEM_DATA_DIR\"] = os.path.dirname(self.live)\n try:\n snap = snapshot.snapshot(os.path.join(self.tmp, \"out2\"))\n finally:\n del os.environ[\"CLAUDE_MEM_DATA_DIR\"]\n self.assertTrue(os.path.exists(snap))\n\n def test_missing_db_is_a_clear_error(self):\n with self.assertRaises(FileNotFoundError):\n snapshot.snapshot(os.path.join(self.tmp, \"out3\"), live=os.path.join(self.tmp, \"nope.db\"))\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":2584,"content_sha256":"323379622ffc8984d3bf2d3710fb1cc8284fd7174ed7e6725dbf5613bea5cdd4"},{"filename":"scripts/tests/test_sync.py","content":"\"\"\"Phase 7.2 tests: shipped-file list, CHECKSUMS.txt, drift detection and --write against temp destinations.\"\"\"\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\n\nimport _paths # noqa: F401\nfrom acr import sync\n\n\nclass Sync(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp(); self.src = os.path.join(self.tmp, \"plugin\", \"skills\", \"agent-cost-report\")\n os.makedirs(os.path.join(self.src, \"scripts\", \"acr\")); os.makedirs(os.path.join(self.src, \"scripts\", \"__pycache__\"))\n for rel, body in ((\"SKILL.md\", \"# skill\\n\"), (\"scripts/acr.py\", \"print(1)\\n\"), (\"scripts/acr/x.py\", \"x = 1\\n\"), (\"scripts/__pycache__/x.pyc\", \"junk\"), (\"README.md\", \"not shipped\\n\")):\n with open(os.path.join(self.src, rel), \"w\") as fh: fh.write(body)\n self.dests = [os.path.join(self.tmp, \"house\"), os.path.join(self.tmp, \"mirror\", \"skills\", \"agent-cost-report\")]\n\n def tearDown(self):\n shutil.rmtree(self.tmp)\n\n def test_shipped_files_and_checksums(self):\n self.assertEqual(sync.shipped_files(self.src), [\"SKILL.md\", \"scripts/acr.py\", \"scripts/acr/x.py\"])\n lines = sync.write_checksums(self.src)\n self.assertEqual(len(lines), 3); self.assertTrue(all(len(l.split(\" \")[0]) == 64 for l in lines))\n r = subprocess.run([\"sha256sum\", \"-c\", sync.CHECKSUMS], cwd=self.src, capture_output=True, text=True)\n self.assertEqual(r.returncode, 0, r.stdout + r.stderr)\n\n def test_drift_then_write(self):\n out = []\n self.assertEqual(sync.run(dests=self.dests, src=self.src, out=out.append), 8) # 4 files x 2 destinations missing\n self.assertTrue(all(l.startswith(\"DRIFT\") for l in out))\n out.clear(); self.assertEqual(sync.run(write=True, dests=self.dests, src=self.src, out=out.append), 0)\n self.assertTrue(all(l.startswith(\"ok\") for l in out))\n self.assertFalse(os.path.exists(os.path.join(self.dests[0], \"README.md\"))) # only shipped files are copied\n self.assertFalse(os.path.exists(os.path.join(self.dests[0], \"scripts\", \"__pycache__\")))\n with open(os.path.join(self.dests[1], \"scripts\", \"acr\", \"x.py\"), \"w\") as fh: fh.write(\"x = 2\\n\")\n self.assertEqual(sync.run(dests=self.dests, src=self.src, out=out.append), 1)\n\n def test_default_destinations_are_the_house_copy_and_four_mirrors(self):\n d = sync.destinations(repo_root=\"/r\", house=\"/h\")\n self.assertEqual(d, [\"/h\"] + [f\"/r/{m}/skills/agent-cost-report\" for m in (\"claude-mem-cursor\", \"claude-mem-grok-bot\", \"cowork\", \"openclaw\")])\n\n def test_cli_exit_code(self):\n r = subprocess.run([sys.executable, _paths.ACR_PY, \"sync-check\", \"--dest\", self.dests[0]], capture_output=True, text=True)\n self.assertEqual(r.returncode, 1, r.stdout + r.stderr); self.assertIn(\"DRIFT\", r.stdout)\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":2909,"content_sha256":"ca1b30d767ddf668bf9ace78e76aa4592eb299a09c1f5f9049e089f610d1ba11"},{"filename":"scripts/tests/test_transcript_encoding.py","content":"\"\"\"Read the UTF-8 transcript contract under native Windows legacy encoding, and keep every text read and\nwrite in the skill on an explicit encoding (the guard runs on every OS).\"\"\"\nimport argparse\nimport ast\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport unittest\nfrom pathlib import Path\n\nimport _paths\n\n\nclass ExplicitEncoding(unittest.TestCase):\n def test_every_text_open_and_text_subprocess_names_an_encoding(self):\n # Without encoding=, Python uses the locale codec: cp936 or cp1252 on Windows, which mangles or rejects\n # the UTF-8 in transcripts, rule files, gh output, reviewed labels, the CSV and the spot-check sheet (#4250).\n missing = []\n for source in [Path(_paths.ACR_PY), *sorted(Path(_paths.SCRIPTS, \"acr\").glob(\"*.py\"))]:\n for node in ast.walk(ast.parse(source.read_text(encoding=\"utf-8\"))):\n if not isinstance(node, ast.Call): continue\n kw = {k.arg: k.value for k in node.keywords}\n if \"encoding\" in kw: continue\n mode = node.args[1] if len(node.args) > 1 else kw.get(\"mode\")\n binary = isinstance(mode, ast.Constant) and isinstance(mode.value, str) and \"b\" in mode.value\n text_open = isinstance(node.func, ast.Name) and node.func.id == \"open\" and not binary\n text_run = any(isinstance(kw.get(k), ast.Constant) and kw[k].value is True for k in (\"text\", \"universal_newlines\"))\n if text_open or text_run: missing.append(f\"{source.name}:{node.lineno}\")\n self.assertEqual(missing, [])\n\n def test_review_apply_accepts_a_reviewed_file_saved_with_a_bom(self):\n from acr import rollup\n tmp = tempfile.mkdtemp(); self.addCleanup(shutil.rmtree, tmp, True)\n shutil.copy(os.path.join(_paths.FIXTURES, \"report-7day.json\"), os.path.join(tmp, \"report.json\"))\n reviewed = dict(reviewed_by=\"Alex\", items=[dict(work_item_id=\"WI-1\", category=\"Feature\", title=\"Résumé — naïve ✓\")])\n with open(os.path.join(tmp, \"reviewed.json\"), \"wb\") as fh: # what a Windows editor saves: UTF-8 with a BOM\n fh.write(b\"\\xef\\xbb\\xbf\" + json.dumps(reviewed, ensure_ascii=False).encode(\"utf-8\"))\n rollup.run_review(argparse.Namespace(out=tmp, apply=os.path.join(tmp, \"reviewed.json\")))\n with open(os.path.join(tmp, \"report.json\"), encoding=\"utf-8\") as fh: item = next(li for li in json.load(fh)[\"line_items\"] if li[\"work_item_id\"] == \"WI-1\")\n self.assertEqual((item[\"reviewed_by\"], item[\"label_source\"], item[\"title\"]), (\"Alex\", \"human\", \"Résumé — naïve ✓\"))\n\n\[email protected](os.name == \"nt\", \"Native Windows default-encoding regression\")\nclass TranscriptEncoding(unittest.TestCase):\n def check_reader(self, kind):\n program = r'''\nimport json, locale, pathlib, sys, tempfile\nsys.path.insert(0, sys.argv[1])\nfrom acr import transcripts, behavior, wins, rules\nencoding = locale.getpreferredencoding(False)\nassert encoding.lower() != \"utf-8\", encoding\nwith tempfile.TemporaryDirectory() as folder:\n source = pathlib.Path(folder) / \"session.jsonl\"\n usage = {\"input_tokens\": 10, \"output_tokens\": 2}\n a = {\"type\": \"assistant\", \"timestamp\": \"2026-09-18T12:00:00Z\", \"sessionId\": \"session\",\n \"cwd\": \"C:/workspace/中文\", \"requestId\": \"request\", \"message\": {\"id\": \"message\", \"usage\": usage,\n \"content\": [{\"type\": \"text\", \"text\": \"完成工作\"}, {\"type\": \"tool_use\", \"id\": \"tool\", \"name\": \"Bash\",\n \"input\": {\"command\": \"gh pr merge 42\"}}]}}\n u = {\"type\": \"user\", \"timestamp\": \"2026-09-18T12:00:01Z\", \"sessionId\": \"session\",\n \"message\": {\"content\": [{\"type\": \"tool_result\", \"tool_use_id\": \"tool\", \"content\": \"合并完成\"}]}}\n source.write_text(\"\\n\".join(json.dumps(x, ensure_ascii=False) for x in (a, u))+\"\\n\", encoding=\"utf-8\")\n if sys.argv[2] == \"codex\":\n events = [{\"type\": \"turn_context\", \"payload\": {\"model\": \"gpt-5-é\"}},\n {\"type\": \"event_msg\", \"timestamp\": \"2026-09-18T12:00:00Z\",\n \"payload\": {\"type\": \"token_count\", \"info\": {\"total_token_usage\": usage}}}]\n source.write_text(\"\\n\".join(json.dumps(x, ensure_ascii=False) for x in events)+\"\\n\", encoding=\"utf-8\")\n rows, _ = transcripts.collect_codex(None, None, pattern=str(source))\n assert len(rows) == 1 and rows[0][\"input\"] == 10 and rows[0][\"output\"] == 2, repr(rows)\n assert rows[0][\"model\"] == \"gpt-5-é\", repr(rows[0][\"model\"])\n elif sys.argv[2] == \"usage\":\n rows, _ = transcripts.collect_claude(None, None, session=\"session\", pattern=str(source))\n assert len(rows) == 1 and rows[0][\"input\"] == 10\n assert rows[0][\"cwd\"] == \"C:/workspace/中文\", repr(rows[0][\"cwd\"])\n elif sys.argv[2] == \"behavior\":\n sessions, _ = behavior.scan(None, None, session=\"session\", pattern=str(source))\n turn = next(iter(sessions[\"session\"][\"turns\"].values()))\n assert turn[\"text\"] == \"完成工作\", repr(turn[\"text\"])\n elif sys.argv[2] == \"wins\":\n items = list(wins.scan_transcripts([str(source)]))\n assert len(items) == 1\n assert items[0][\"cwd\"] == \"C:/workspace/中文\", repr(items[0][\"cwd\"])\n assert \"合并完成\" in items[0][\"result\"], repr(items[0][\"result\"])\n else:\n rule = pathlib.Path(folder) / \"rules.md\"\n rule.write_text(\"# 保留证据 (HARD — Alex 2026-09-18)\\n\", encoding=\"utf-8\")\n items = rules.scan_rule_files(folder)\n assert len(items) == 1 and items[0][\"name\"] == \"保留证据\", repr(items)\n'''\n result = subprocess.run(\n [sys.executable, \"-X\", \"utf8=0\", \"-c\", program, _paths.SCRIPTS, kind],\n env=dict(os.environ, PYTHONUTF8=\"0\"), capture_output=True,\n )\n self.assertEqual(result.returncode, 0, result.stderr.decode(\"utf-8\", errors=\"replace\"))\n\n def test_usage_retains_unicode_workspace(self):\n self.check_reader(\"usage\")\n\n def test_behavior_retains_unicode_evidence(self):\n self.check_reader(\"behavior\")\n\n def test_wins_retain_unicode_workspace_and_result(self):\n self.check_reader(\"wins\")\n\n def test_rule_names_retain_unicode(self):\n self.check_reader(\"rules\")\n\n def test_codex_retains_unicode_model(self):\n self.check_reader(\"codex\")\n","content_type":"text/x-python; charset=utf-8","language":"python","size":6313,"content_sha256":"a44687638f13da7dd413e2b2cd62a9bbaacdd81c98600ddc287d51ba995ee841"},{"filename":"scripts/tests/test_transcripts.py","content":"import datetime as dt\nimport json\nimport os\nimport platform\nimport shutil\nimport tempfile\nimport unittest\n\nimport _paths\nfrom acr import period, transcripts\nfrom acr.period import PT\n\n\nclass Collector(unittest.TestCase):\n \"\"\"Fixtures are copied into a temp dir so their mtime is 'now' (the reader prefilters on mtime).\"\"\"\n\n @classmethod\n def setUpClass(cls):\n cls.tmp = tempfile.mkdtemp(prefix=\"acr-tests-\")\n shutil.copytree(os.path.join(_paths.FIXTURES, \"projects\"), os.path.join(cls.tmp, \"projects\"), copy_function=shutil.copy)\n shutil.copytree(os.path.join(_paths.FIXTURES, \"codex\"), os.path.join(cls.tmp, \"codex\"), copy_function=shutil.copy)\n cls.claude_glob = os.path.join(cls.tmp, \"projects\", \"**\", \"*.jsonl\")\n cls.codex_glob = os.path.join(cls.tmp, \"codex\", \"sessions\", \"**\", \"*.jsonl\")\n cls.now = dt.datetime(2026, 9, 25, 16, 42, tzinfo=PT)\n cls.window = period.resolve_window(\"2026-09-18\", \"2026-09-19\", now=cls.now)\n\n @classmethod\n def tearDownClass(cls):\n shutil.rmtree(cls.tmp, ignore_errors=True)\n\n def collect(self, **kw):\n return transcripts.collect(kw.pop(\"window\", self.window), claude_glob=self.claude_glob,\n codex_glob=self.codex_glob, **kw)\n\n def by_msg(self, doc):\n out = {}\n for r in doc[\"rows\"]:\n if r[\"src\"] == \"claude-code\":\n out.setdefault((r[\"session\"], r[\"input\"]), r)\n return out\n\n def test_dedup_on_message_id_and_request_id(self):\n doc = self.collect()\n claude = [r for r in doc[\"rows\"] if r[\"src\"] == \"claude-code\"]\n # sess-aaaa: msg_1 (twice -> once), msg_3, msg_4; msg_2 excluded (t == end). sess-bbbb: msg_5.\n self.assertEqual(len(claude), 4)\n self.assertEqual(doc[\"collector\"][\"dedup_dropped\"], 1)\n self.assertEqual(sum(1 for r in claude if r[\"input\"] == 10), 1)\n\n def test_window_edges_start_included_end_excluded(self):\n doc = self.collect()\n inputs = sorted(r[\"input\"] for r in doc[\"rows\"] if r[\"src\"] == \"claude-code\")\n self.assertIn(10, inputs) # msg_1 at exactly 2026-09-18T07:00:00Z == start\n self.assertNotIn(1000, inputs) # msg_2 at exactly 2026-09-19T07:00:00Z == end\n # start row rendered in PT\n row = next(r for r in doc[\"rows\"] if r[\"input\"] == 10)\n self.assertEqual(row[\"ts\"], \"2026-09-18T00:00:00-07:00\")\n\n def test_cache_split_direct_ephemeral_5m_field(self):\n row = self.by_msg(self.collect())[(\"sess-aaaa\", 10)]\n self.assertEqual((row[\"cache_write\"], row[\"cache_write_1h\"], row[\"cache_write_5m\"]), (100, 40, 60))\n\n def test_cache_split_fallback_subtraction(self):\n rows = self.by_msg(self.collect())\n r3 = rows[(\"sess-aaaa\", 1)] # cache_creation has only ephemeral_1h_input_tokens\n self.assertEqual((r3[\"cache_write\"], r3[\"cache_write_1h\"], r3[\"cache_write_5m\"]), (100, 30, 70))\n r4 = rows[(\"sess-aaaa\", 3)] # no cache_creation block at all\n self.assertEqual((r4[\"cache_write\"], r4[\"cache_write_1h\"], r4[\"cache_write_5m\"]), (100, 0, 100))\n\n def test_row_fields_and_extensions(self):\n rows = self.by_msg(self.collect())\n r = rows[(\"sess-bbbb\", 50)]\n self.assertEqual(set(r), {\"src\", \"file\", \"dir\", \"cwd\", \"session\", \"sidechain\", \"model\", \"ts\", \"input\", \"output\",\n \"cache_write\", \"cache_write_1h\", \"cache_write_5m\", \"cache_read\", \"cost_usd_reported\", \"device\"})\n self.assertEqual(r[\"dir\"], \"-workspace-proj-b\")\n self.assertEqual(r[\"cwd\"], \"/workspace/proj-b\")\n self.assertTrue(r[\"sidechain\"])\n self.assertEqual(r[\"device\"], \"local\")\n self.assertEqual(r[\"cost_usd_reported\"], 0.0123)\n self.assertEqual(r[\"model\"], \"claude-fable-5-1\")\n self.assertIsNone(rows[(\"sess-aaaa\", 10)][\"cost_usd_reported\"])\n\n def test_collector_block(self):\n doc = self.collect()\n c = doc[\"collector\"]\n self.assertEqual(c[\"host_label\"], platform.node())\n self.assertNotRegex(c[\"host_label\"], r\"^[0-9a-f]{8}-[0-9a-f]{4}-\") # never a device UUID\n self.assertEqual(c[\"files_seen\"], 3)\n self.assertEqual(c[\"files_seen_by_src\"], {\"claude-code\": 2, \"codex\": 1})\n self.assertEqual(c[\"rows\"], len(doc[\"rows\"]))\n self.assertEqual(c[\"window\"], doc[\"window\"])\n self.assertEqual(doc[\"window\"][\"start_pt\"], \"2026-09-18\")\n self.assertFalse(doc[\"window\"][\"partial_last_day\"])\n self.assertEqual(c[\"glob\"], [self.claude_glob, self.codex_glob])\n\n def test_codex_cumulative_counter_becomes_per_event_growth_in_window(self):\n doc = self.collect()\n codex = [r for r in doc[\"rows\"] if r[\"src\"] == \"codex\"]\n # two token_count events inside the window (the 2026-09-19T07:00Z one == end is excluded); each row is the growth since the previous event\n self.assertEqual(len(codex), 2)\n self.assertEqual([(r[\"input\"], r[\"output\"], r[\"cache_read\"]) for r in codex], [(100 - 40, 10, 40), (400 - 160, 70, 160)])\n self.assertEqual((sum(r[\"input\"] for r in codex), sum(r[\"output\"] for r in codex), sum(r[\"cache_read\"] for r in codex)), (500 - 200, 80, 200))\n r = codex[0]\n self.assertEqual(r[\"model\"], \"gpt-5-codex\")\n self.assertEqual(r[\"session\"], \"rollout-codex-1.jsonl\")\n self.assertEqual(r[\"device\"], \"local\")\n self.assertEqual(r[\"cache_write_5m\"], 0)\n\n def test_codex_usage_before_the_window_is_subtracted(self):\n d = os.path.join(self.tmp, \"codex\", \"sessions\", \"2026\", \"09\", \"17\"); os.makedirs(d, exist_ok=True); self.addCleanup(shutil.rmtree, d, ignore_errors=True)\n ev = lambda t, i, c, o: json.dumps(dict(timestamp=t, type=\"event_msg\", payload=dict(type=\"token_count\", info=dict(total_token_usage=dict(input_tokens=i, cached_input_tokens=c, output_tokens=o)))))\n with open(os.path.join(d, \"rollout-boundary.jsonl\"), \"w\") as fh:\n fh.write(json.dumps(dict(timestamp=\"2026-09-17T20:00:00.000Z\", type=\"turn_context\", payload=dict(model=\"gpt-5-codex\"))) + \"\\n\")\n fh.write(ev(\"2026-09-17T20:00:05.000Z\", 300000, 100000, 50000) + \"\\n\") # before the window: cumulative 300k/100k/50k\n fh.write(ev(\"2026-09-18T15:00:00.000Z\", 300000, 100000, 50000) + \"\\n\") # no growth: no row\n fh.write(ev(\"2026-09-18T16:00:00.000Z\", 350000, 120000, 60000) + \"\\n\") # +50k in (+20k cached), +10k out\n fh.write(ev(\"2026-09-18T23:00:00.000Z\", 360000, 120000, 61000) + \"\\n\") # +10k in, +1k out\n doc = self.collect()\n rows = [r for r in doc[\"rows\"] if r[\"src\"] == \"codex\" and r[\"session\"] == \"rollout-boundary.jsonl\"]\n self.assertEqual([(r[\"input\"], r[\"cache_read\"], r[\"output\"]) for r in rows], [(30000, 20000, 10000), (10000, 0, 1000)])\n self.assertEqual(sum(r[\"input\"] + r[\"cache_read\"] for r in rows), 360000 - 300000) # only in-window growth, never the pre-window 300k\n\n def test_session_filter_has_no_period_filter(self):\n block = period.session_block(\"sess-aaaa\", now=self.now)\n doc = self.collect(window=None, session=\"sess-aaaa\", window_block=block)\n sessions = {r[\"session\"] for r in doc[\"rows\"]}\n self.assertEqual(sessions, {\"sess-aaaa\"})\n inputs = sorted(r[\"input\"] for r in doc[\"rows\"])\n self.assertEqual(inputs, [1, 3, 10, 1000]) # msg_2 (t == old end) is in: no window\n self.assertEqual(doc[\"collector\"][\"dedup_dropped\"], 1)\n self.assertEqual(doc[\"window\"][\"session\"], \"sess-aaaa\")\n self.assertIsNone(doc[\"window\"][\"start_pt\"])\n\n def test_session_filter_other_session(self):\n doc = self.collect(window=None, session=\"sess-bbbb\", window_block=period.session_block(\"sess-bbbb\", now=self.now))\n self.assertEqual([r[\"session\"] for r in doc[\"rows\"]], [\"sess-bbbb\"])\n\n def test_window_outside_fixture_dates_yields_no_rows(self):\n w = period.resolve_window(\"2026-09-20\", \"2026-09-21\", now=self.now)\n self.assertEqual(self.collect(window=w)[\"rows\"], [])\n\n def test_session_without_window_block_builds_one(self):\n doc = self.collect(window=None, session=\"sess-bbbb\") # no window_block passed\n self.assertEqual(doc[\"window\"][\"session\"], \"sess-bbbb\")\n self.assertIsNone(doc[\"window\"][\"start_pt\"])\n self.assertEqual([r[\"session\"] for r in doc[\"rows\"]], [\"sess-bbbb\"])\n\n def test_needs_window_or_session(self):\n with self.assertRaises(ValueError):\n transcripts.collect(None, claude_glob=self.claude_glob, codex_glob=self.codex_glob)\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":8630,"content_sha256":"2c39ce509e7293e95ffa81f766f46792c76a0497f491806d797e9ec76a822d52"},{"filename":"scripts/tests/test_wins.py","content":"import datetime as dt\nimport json\nimport os\nimport shutil\nimport tempfile\nimport unittest\n\nimport _paths\nfrom acr import wins\nfrom acr.period import PT\n\nS = int(dt.datetime(2026, 9, 18, tzinfo=PT).timestamp() * 1000)\nE = int(dt.datetime(2026, 9, 21, tzinfo=PT).timestamp() * 1000)\nH = 3600 * 1000\nDAYS = [\"2026-09-18\", \"2026-09-19\", \"2026-09-20\"]\n\n\ndef iso(ms):\n return dt.datetime.fromtimestamp(ms / 1000, dt.timezone.utc).isoformat().replace(\"+00:00\", \"Z\")\n\n\ndef usage_row(cs, ms, x1e6, out=100):\n return dict(session=cs, ts_ms=ms, x1e6=x1e6, input=1000, output=out, cache_write_5m=0, cache_write_1h=0, cache_read=0)\n\n\ndef tool_lines(session, ms, cmd, result=\"\", is_error=False, tid=\"toolu_1\"):\n a = dict(type=\"assistant\", timestamp=iso(ms), sessionId=session, cwd=\"/w/p\", message=dict(role=\"assistant\", content=[dict(type=\"tool_use\", id=tid, name=\"Bash\", input=dict(command=cmd))]))\n u = dict(type=\"user\", timestamp=iso(ms + 1000), sessionId=session, message=dict(role=\"user\", content=[dict(type=\"tool_result\", tool_use_id=tid, content=result, is_error=is_error)]),\n toolUseResult=dict(stdout=result, stderr=\"\"))\n return [json.dumps(a), json.dumps(u)]\n\n\ndef fake_gh(table):\n calls = []\n def gh(number, repo):\n calls.append((number, repo)); return table.get(f\"{repo}#{number}\")\n gh.calls = calls; return gh\n\n\nclass Commands(unittest.TestCase):\n def test_classify(self):\n c = wins.classify_command(\"gh pr merge 12 --repo o/r --squash\")\n self.assertEqual((c[\"kind\"], c[\"number\"], c[\"repo\"]), (\"pr\", 12, \"o/r\"))\n c = wins.classify_command(\"cd /x && gh pr merge https://github.com/o/r/pull/7 --merge\")\n self.assertEqual((c[\"number\"], c[\"repo\"]), (7, \"o/r\"))\n self.assertEqual(wins.classify_command(\"gh pr merge --auto\")[\"number\"], None)\n self.assertEqual(wins.classify_command(\"npm publish --access public\")[\"how\"], \"npm publish\")\n self.assertEqual(wins.classify_command(\"gh release create v1.2.3 --notes x\")[\"tag\"], \"v1.2.3\")\n self.assertEqual(wins.classify_command(\"git push origin v1.2.3\")[\"tag\"], \"v1.2.3\")\n self.assertEqual(wins.classify_command(\"git push --tags\")[\"tag\"], None)\n self.assertIsNone(wins.classify_command(\"git push origin main\"))\n self.assertIsNone(wins.classify_command(\"grep -l 'gh pr merge' *.jsonl | head\")) # quoted prose\n self.assertIsNone(wins.classify_command(\"cat > f.py \u003c\u003c'PY'\\nnpm publish\\ngh pr merge 1\\nPY\")) # heredoc body\n self.assertIsNone(wins.classify_command(\"echo done\"))\n\n def test_trailers(self):\n msg = \"feat: x\\n\\nbody\\n\\nClaude-Session: 0e3f6cc9-3feb-4e4a-b20e-cc3704ef868d\\nCo-Authored-By: x\"\n self.assertEqual(wins.session_trailers(msg), [\"0e3f6cc9-3feb-4e4a-b20e-cc3704ef868d\"])\n self.assertEqual(wins.session_trailers(\"Session: 0e3f6cc9-3feb-4e4a-b20e-cc3704ef868d\"), []) # G13: Session: ignored\n self.assertEqual(wins.session_trailers(\"Claude-Session: https://x/0e3f6cc9\"), []) # non-bare rejected\n self.assertEqual(wins.session_trailers(\"Claude-Session: abc def\"), []); self.assertEqual(wins.session_trailers(\"Claude-Session: (abc)\"), [])\n self.assertEqual(wins.session_trailers(\"Claude-Session: a\\nClaude-Session: a\\nClaude-Session: b\"), [\"a\", \"b\"])\n\n def test_no_fallback_estimate_anywhere(self):\n for f in (\"wins.py\", \"rollup.py\", \"costs.py\"):\n with open(os.path.join(_paths.SCRIPTS, \"acr\", f)) as fh: src = fh.read()\n self.assertNotIn(\"fallback_estimate\", src, f); self.assertNotIn(\"lineage\", src.lower().replace(\"no lineage fallback\", \"\"), f)\n\n def test_praise_stub(self):\n self.assertEqual(wins.detect_praise(), []); self.assertEqual(wins.detect_praise(prompts=[\"boom love it\"]), [])\n\n\nclass Transcripts(unittest.TestCase):\n def setUp(self):\n self.tmp = tempfile.mkdtemp(prefix=\"acr-wins-\")\n\n def tearDown(self):\n shutil.rmtree(self.tmp, ignore_errors=True)\n\n def write(self, name, lines):\n p = os.path.join(self.tmp, name)\n with open(p, \"w\") as fh: fh.write(\"\\n\".join(lines) + \"\\n\")\n return p\n\n def test_scan_success_error_and_window(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"gh pr merge 5 --repo o/r\", \"\", tid=\"t1\")\n + tool_lines(\"cs1\", S + 3 * H, \"gh pr merge 6 --repo o/r\", \"GraphQL: Pull request not mergeable\", is_error=False, tid=\"t2\")\n + tool_lines(\"cs1\", S + 4 * H, \"gh pr merge 7 --repo o/r\", \"\", is_error=True, tid=\"t3\")\n + tool_lines(\"cs1\", E + H, \"gh pr merge 8 --repo o/r\", \"\", tid=\"t4\")\n + tool_lines(\"cs1\", S + 5 * H, \"npm publish\", \"npm notice Publishing to https://registry.npmjs.org/\\n+ [email protected]\", tid=\"t5\")\n + ['{\"type\":\"assistant\",\"message\":{\"usage\":{}}}', \"not json\"])\n got = list(wins.scan_transcripts([f], S, E))\n self.assertEqual([(c[\"kind\"], c.get(\"number\")) for c in got], [(\"pr\", 5), (\"publish\", None)])\n self.assertIn(\"[email protected]\", got[1][\"result\"])\n\n\nclass Build(Transcripts):\n def sessions(self):\n return {\"cs1\": dict(project=\"claude-mem\", memory_session_id=\"m1\", has_transcript=True, observer_tokens=100, cwd=\"/w/p\"),\n \"cs2\": dict(project=\"claude-mem\", memory_session_id=\"m2\", has_transcript=False, observer_tokens=1000, cwd=None)}\n\n def ship(self, n=4125, ms=S + 6 * H):\n return [dict(id=9, title=f\"PR #{n} merged to main: x\", ts_ms=ms, project=\"claude-mem\", memory_session_id=\"m1\", content_session_id=\"cs1\")]\n\n def build(self, files=(), ship_obs=(), rows=None, gh=None, ratio=None, remote=lambda cwd: \"o/r\", gh_list=None, repos=(\"o/r\",)):\n return wins.build(list(files), S, E, list(ship_obs), self.sessions(), rows or {}, ratio, DAYS, gh=gh or fake_gh({}), remote=remote,\n use_gh=(gh is not None or gh_list is not None), repos=repos, gh_list=gh_list)\n\n def test_no_link_means_every_usd_null(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"gh pr merge 5 --repo o/r\"))\n block, by_day = self.build([f], self.ship(), rows={\"cs1\": [usage_row(\"cs1\", S + H, 1_000_000)]})\n self.assertEqual(len(block[\"items\"]), 2); self.assertEqual(block[\"cost_status\"], \"unmeasured\")\n self.assertTrue(all(i[\"usd\"] is None and i[\"cost_status\"] == \"unmeasured\" for i in block[\"items\"]))\n self.assertIsNone(block[\"attribution_method\"]); self.assertIsNone(block[\"total_attributed_usd\"]); self.assertIsNone(block[\"unattributed_usd\"])\n self.assertEqual(block[\"praise_n\"], 0); self.assertEqual([d[\"day_pt\"] for d in by_day], DAYS)\n self.assertEqual([d[\"count\"] for d in by_day], [2, 0, 0]); self.assertTrue(all(d[\"usd\"] is None for d in by_day))\n by_key = {i[\"key\"]: i for i in block[\"items\"]}\n self.assertEqual(by_key[\"o/r#4125\"][\"title\"], \"PR #4125 merged to main: x\") # observation title, never hand-written\n self.assertEqual(by_key[\"o/r#5\"][\"title\"], \"o/r#5\") # no title source yet: the key itself\n\n def test_pr_in_transcript_and_observation_counts_once(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"gh pr merge 4125 --repo o/r\"))\n block, _ = self.build([f], self.ship(4125))\n self.assertEqual(len(block[\"items\"]), 1); self.assertEqual(block[\"items\"][0][\"key\"], \"o/r#4125\")\n self.assertEqual(set(block[\"items\"][0][\"sources\"]), {\"transcript\", \"ship_observation\"}); self.assertEqual(block[\"items\"][0][\"evidence_ids\"], [9])\n\n def test_observation_repo_from_remote_of_project_cwd(self):\n block, _ = self.build([], self.ship(1), remote=lambda cwd: \"thedotmack/claude-mem\" if cwd == \"/w/p\" else None)\n self.assertEqual(block[\"items\"][0][\"key\"], \"thedotmack/claude-mem#1\")\n block, _ = self.build([], self.ship(1), remote=lambda cwd: None)\n self.assertEqual(block[\"items\"][0][\"key\"], \"claude-mem#1\")\n\n def test_gh_confirmation_window_and_not_merged(self):\n merged_in = iso(S + 7 * H); merged_out = iso(E + 5 * H)\n gh = fake_gh({\"o/r#1\": dict(mergedAt=merged_in, url=\"u1\", title=\"T1\", commits=[]), \"o/r#2\": dict(mergedAt=merged_out, url=\"u2\", title=\"T2\", commits=[]),\n \"o/r#3\": dict(mergedAt=None, url=\"u3\", title=\"T3\", commits=[])})\n obs = self.ship(1) + self.ship(2) + self.ship(3)\n block, _ = self.build([], obs, gh=gh)\n self.assertEqual([i[\"key\"] for i in block[\"items\"]], [\"o/r#1\"]); self.assertTrue(block[\"items\"][0][\"confirmed\"])\n self.assertEqual((block[\"items\"][0][\"title\"], block[\"items\"][0][\"url\"]), (\"T1\", \"u1\"))\n self.assertEqual(sorted(d[\"reason\"] for d in block[\"dropped\"]), [\"gh pr view: not merged\", \"outside window\"])\n self.assertEqual(len(gh.calls), 3)\n\n def test_trailer_links_and_shared_turn_assigned_once(self):\n c = lambda sid: [dict(messageHeadline=\"x\", messageBody=f\"body\\n\\nClaude-Session: {sid}\")]\n gh = fake_gh({\"o/r#1\": dict(mergedAt=iso(S + 5 * H), url=\"u\", title=\"first\", commits=c(\"cs1\")),\n \"o/r#2\": dict(mergedAt=iso(S + 9 * H), url=\"u\", title=\"second\", commits=c(\"cs1\") + [dict(messageHeadline=\"y\", messageBody=\"Session: cs2\")])})\n rows = {\"cs1\": [usage_row(\"cs1\", S + H, 1_000_000), usage_row(\"cs1\", S + 2 * H, 2_000_000), usage_row(\"cs1\", S + 7 * H, 4_000_000), usage_row(\"cs1\", S + 20 * H, 8_000_000)]}\n block, by_day = self.build([], self.ship(1) + self.ship(2), rows=rows, gh=gh)\n w1, w2 = block[\"items\"]\n self.assertEqual((w1[\"usd\"], w2[\"usd\"]), (3.0, 4.0)) # each turn once; the turn after both wins is unattributed\n self.assertEqual((w1[\"turns_n\"], w2[\"turns_n\"], w1[\"sessions_n\"]), (2, 1, 1))\n self.assertEqual(block[\"cost_status\"], \"session_linked\"); self.assertEqual(block[\"total_attributed_usd\"], 7.0)\n self.assertEqual(w1[\"cost_label\"], \"ESTIMATED · session-linked\"); self.assertEqual(w1[\"cost_basis\"], \"estimated_usage\")\n self.assertEqual(w2[\"linked_sessions\"], [\"cs1\"]) # the `Session: cs2` line was ignored (G13)\n self.assertEqual(w1[\"tokens\"][\"input\"], 2000); self.assertEqual(by_day[0][\"usd\"], 7.0)\n\n def test_replica_session_win_is_extrapolated_only_with_link(self):\n gh = fake_gh({\"o/r#1\": dict(mergedAt=iso(S + 5 * H), url=\"u\", title=\"t\", commits=[dict(messageHeadline=\"x\", messageBody=\"Claude-Session: cs2\")])})\n block, _ = self.build([], self.ship(1), gh=gh, ratio=2.0) # 2 micro-dollars per observer token\n w = block[\"items\"][0]\n self.assertEqual((w[\"cost_status\"], w[\"cost_basis\"], w[\"usd\"]), (\"session_linked\", \"extrapolated\", 0.0))\n self.assertIn(\"EXTRAPOLATED (low confidence)\", w[\"cost_label\"])\n block, _ = self.build([], self.ship(1), gh=gh, ratio=2.0)\n self.assertEqual(block[\"items\"][0][\"usd\"], round(1000 * 2.0 / 1e6, 2))\n gh2 = fake_gh({\"o/r#1\": dict(mergedAt=iso(S + 5 * H), url=\"u\", title=\"t\", commits=[])})\n block, _ = self.build([], self.ship(1), gh=gh2, ratio=2.0)\n self.assertEqual((block[\"items\"][0][\"cost_status\"], block[\"items\"][0][\"usd\"], block[\"items\"][0][\"cost_basis\"]), (\"unmeasured\", None, None))\n\n def test_finished_work_is_not_a_win(self):\n block, by_day = self.build([], []) # completed sessions exist in sessions(), but no merge/publish evidence\n self.assertEqual(block[\"items\"], []); self.assertEqual([d[\"count\"] for d in by_day], [0, 0, 0])\n\n # ---- read-only GitHub merged-PR source ----\n def test_gh_list_adds_wins_dedupes_and_labels_source(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"gh pr merge 4125 --repo o/r\"))\n lister = lambda repo, s, e: ([dict(number=4125, title=\"from github\", url=\"https://github.com/o/r/pull/4125\", ts_ms=S + 3 * H),\n dict(number=4200, title=\"only on github\", url=\"https://github.com/o/r/pull/4200\", ts_ms=S + 30 * H)], \"ok\")\n block, by_day = self.build([f], self.ship(4125), gh_list=lister)\n keys = {i[\"key\"]: i for i in block[\"items\"]}\n self.assertEqual(sorted(keys), [\"o/r#4125\", \"o/r#4200\"]) # 4125 counted once across three sources\n self.assertEqual(set(keys[\"o/r#4125\"][\"sources\"]), {\"transcript\", \"ship_observation\", wins.GH_SOURCE}); self.assertTrue(keys[\"o/r#4125\"][\"confirmed\"])\n self.assertEqual((keys[\"o/r#4200\"][\"sources\"], keys[\"o/r#4200\"][\"title\"], keys[\"o/r#4200\"][\"usd\"], keys[\"o/r#4200\"][\"cost_status\"]), ([wins.GH_SOURCE], \"only on github\", None, \"unmeasured\"))\n src = block[\"sources\"][wins.GH_SOURCE]; self.assertEqual((src[\"status\"], src[\"found\"], src[\"repos\"][\"o/r\"][\"status\"]), (\"ok\", 2, \"ok\"))\n self.assertEqual([d[\"count\"] for d in by_day], [1, 1, 0])\n\n def test_gh_list_unavailable_never_zero_and_report_completes(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"gh pr merge 4125 --repo o/r\"))\n for reason in (\"gh missing\", \"unauthenticated\", \"rate-limited\"):\n block, _ = self.build([f], gh_list=lambda repo, s, e, r=reason: ([], r))\n src = block[\"sources\"][wins.GH_SOURCE]\n self.assertEqual((src[\"status\"], src[\"repos\"][\"o/r\"][\"status\"], src[\"repos\"][\"o/r\"][\"found\"]), (\"unavailable\", reason, None), reason)\n self.assertEqual(len(block[\"items\"]), 1) # the other sources still ship\n block, _ = self.build([f], gh=fake_gh({}), gh_list=None)\n self.assertEqual(block[\"sources\"][wins.GH_SOURCE][\"repos\"][\"o/r\"][\"status\"], \"unavailable (gh disabled for this run)\")\n\n def test_gh_merged_prs_window_edges_and_failures(self):\n rows = [dict(number=1, title=\"a\", url=\"u1\", mergedAt=iso(S)), dict(number=2, title=\"b\", url=\"u2\", mergedAt=iso(E)), dict(number=3, title=\"c\", url=\"u3\", mergedAt=iso(E - 1000)), dict(number=4, title=\"no merge\", url=\"u4\", mergedAt=None)]\n ok = lambda cmd, **k: type(\"R\", (), dict(returncode=0, stdout=json.dumps(rows), stderr=\"\"))()\n out, st = wins.gh_merged_prs(\"o/r\", S, E, run=ok)\n self.assertEqual((st, [x[\"number\"] for x in out]), (\"ok\", [1, 3])) # start inclusive, end exclusive, unmerged dropped\n def missing(cmd, **k): raise FileNotFoundError(\"gh\")\n self.assertEqual(wins.gh_merged_prs(\"o/r\", S, E, run=missing), ([], \"gh missing\"))\n auth = lambda cmd, **k: type(\"R\", (), dict(returncode=4, stdout=\"\", stderr=\"To get started with GitHub CLI, please run: gh auth login\"))()\n self.assertEqual(wins.gh_merged_prs(\"o/r\", S, E, run=auth)[1], \"unauthenticated\")\n rl = lambda cmd, **k: type(\"R\", (), dict(returncode=1, stdout=\"\", stderr=\"GraphQL: API rate limit exceeded for user\"))()\n self.assertEqual(wins.gh_merged_prs(\"o/r\", S, E, run=rl)[1], \"rate-limited\")\n junk = lambda cmd, **k: type(\"R\", (), dict(returncode=0, stdout=\"not json\", stderr=\"\"))()\n self.assertEqual(wins.gh_merged_prs(\"o/r\", S, E, run=junk)[1], \"gh error (unparseable output)\")\n seen = []\n def spy(cmd, **k): seen.append(cmd); return ok(cmd)\n wins.gh_merged_prs(\"o/r\", S, E, run=spy)\n self.assertEqual(seen[0][:6], [\"gh\", \"pr\", \"list\", \"--repo\", \"o/r\", \"--state\"]); self.assertEqual(seen[0][1:3], [\"pr\", \"list\"]); self.assertNotIn(\"merge\", seen[0][2]) # read-only: list, never merge\n\n def test_publish_from_transcript_and_observation_dedup(self):\n f = self.write(\"a.jsonl\", tool_lines(\"cs1\", S + 2 * H, \"npm publish\", \"npm notice Publishing to https://registry.npmjs.org/\\n+ [email protected]\"))\n obs = [dict(id=3, title=\"claude-mem 13.25.3 published to npm\", ts_ms=S + 3 * H, project=\"claude-mem/wt\", memory_session_id=\"m1\", content_session_id=\"cs1\")]\n block, _ = self.build([f], obs)\n self.assertEqual([i[\"key\"] for i in block[\"items\"]], [\"[email protected]\"]); self.assertEqual(block[\"items\"][0][\"kind\"], \"publish\")\n\n\nif __name__ == \"__main__\":\n unittest.main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":16229,"content_sha256":"f0d9bd9b031876277addd413b05efb5ec762932254b24975e2bc16b93562dbc7"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Agent Cost Report","type":"text"}]},{"type":"paragraph","content":[{"text":"Claude-Mem / Claude Code skill.","type":"text","marks":[{"type":"strong"}]},{"text":" Runtime is the ","type":"text"},{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" pipeline (transcripts → tokens → dollars → Timing-style report) plus a progressive Mem Search review pass that confirms the drafted labels. The Notion draft is SPEC history only — never the product, never the runtime, never the ship vehicle.","type":"text"}]},{"type":"paragraph","content":[{"text":"Resolve the absolute directory containing this ","type":"text"},{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"; all helper paths are relative to that directory. ","type":"text"},{"text":"${CLAUDE_SKILL_DIR}","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the shortcut: ","type":"text"},{"text":"python3 \"${CLAUDE_SKILL_DIR}/scripts/acr.py\" …","type":"text","marks":[{"type":"code_inline"}]},{"text":". Python 3.9+ standard library only, with IANA timezone data for ","type":"text"},{"text":"America/Los_Angeles","type":"text","marks":[{"type":"code_inline"}]},{"text":"; no pip installs. ","type":"text"},{"text":"tzdata","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the only exception to the no-pip-installs rule, and only when ","type":"text"},{"text":"acr.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" exits saying that timezone data is unavailable (stock Windows Python ships none): show the user the lines it printed and ask (AskUserQuestion) before installing it. On a yes, run the line for your shell (the plain quoted line in Bash or cmd, the ","type":"text"},{"text":"PowerShell:","type":"text","marks":[{"type":"code_inline"}]},{"text":" line in PowerShell), then rerun the failed command. Setting ","type":"text"},{"text":"PYTHONTZPATH","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead needs no install. The look lives in ","type":"text"},{"text":"scripts/acr/render.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", never here.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Purpose","type":"text"}]},{"type":"paragraph","content":[{"text":"Turn Claude-Mem activity into a ","type":"text"},{"text":"manager-readable cost and failure report","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Product idea: a reusable skill that searches Claude-Mem via Mem Search, reconstructs real units of work, assigns cost and failure categories, and renders a printable report.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Insight north star","type":"text"}]},{"type":"paragraph","content":[{"text":"The headline is dollars, to two decimals, labeled. The dollars come from Claude Code transcripts (exact per-reply token usage) priced at OpenRouter public list prices, so they are ","type":"text"},{"text":"ESTIMATED","type":"text","marks":[{"type":"strong"}]},{"text":". Measured provider spend appears only when a sanctioned source gives it. Directly under the dollars: what the mistakes cost, what shipped, and both on one time axis.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Questions the report must answer","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What work was completed?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What did each outcome cost?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What was wasted through looping, hedging, wrong turns, rework, or poor routing?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Were any unauthorized actions attempted?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What should the manager change next?","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Primary unit = cost per completed outcome","type":"text","marks":[{"type":"strong"}]},{"text":" (not cost per observation).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Agent cost report\" / \"cost per outcome\" / \"failure economics\" / \"was this session worth it\" / \"what did the agents cost this week\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After a real Mem session dig when leadership needs outcome economics","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sample / ship packs that need self-contained HTML + JSON + CSV + evidence","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Memory dig mechanics: the claude-mem ","type":"text"},{"text":"mem-search","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill (progressive recall).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Default scope (ALWAYS)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unless the user names a ","type":"text"},{"text":"specific session / range / project","type":"text","marks":[{"type":"strong"}]},{"text":", the window is the ","type":"text"},{"text":"last 7 full days in PT, not counting today","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"end","type":"text","marks":[{"type":"code_inline"}]},{"text":" = the PT midnight that started today (exclusive), ","type":"text"},{"text":"start","type":"text","marks":[{"type":"code_inline"}]},{"text":" = ","type":"text"},{"text":"end − 7 days","type":"text","marks":[{"type":"code_inline"}]},{"text":". The default window never contains a partial day (G3, Alex 2026-09-25).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Explicit windows: ","type":"text"},{"text":"--start YYYY-MM-DD --end YYYY-MM-DD","type":"text","marks":[{"type":"code_inline"}]},{"text":" (PT calendar days, end exclusive). An explicit ","type":"text"},{"text":"--end","type":"text","marks":[{"type":"code_inline"}]},{"text":" later than today marks the last day \"partial, generated HH:MM PT\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"One session: ","type":"text"},{"text":"--session \u003ccontent_session_id>","type":"text","marks":[{"type":"code_inline"}]},{"text":". One project plus a period: ","type":"text"},{"text":"--project \u003cname> --start … --end …","type":"text","marks":[{"type":"code_inline"}]},{"text":" (worktrees of the project are included).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The report ","type":"text"},{"text":"Scope","type":"text","marks":[{"type":"strong"}]},{"text":" strip shows the PT range, and Details list every session id in scope.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Progressive Mem Search (ALWAYS)","type":"text"}]},{"type":"paragraph","content":[{"text":"Follow the claude-mem ","type":"text"},{"text":"mem-search","type":"text","marks":[{"type":"code_inline"}]},{"text":" three layers. Keep spend light.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search","type":"text","marks":[{"type":"strong"}]},{"text":" — get an index of IDs (titles, types, token hints).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Timeline","type":"text","marks":[{"type":"strong"}]},{"text":" — only around anchors you care about.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Observations","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"get_observations","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the filtered IDs you will cite as evidence.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Recipe:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Resolve scope (default: the last 7 full PT days).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search → collect IDs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Timeline for thin context only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Observations for intended / actual / outcome / waste / rework / blocked / unauthorized / status.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Group into named work items + failure events.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Calculate line-item costs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Render HTML + optional PDF (+ json/csv/evidence).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep evidence IDs in the appendix — do not dump entire timelines into the main report.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"ADHD process bullets:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search first → pick IDs → timeline only if context is thin → fetch only needed obs.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Work-item titles are invented for managers (\"Restore search after Chroma crash-loop\"); observation titles stay evidence-only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Evidence appendix lists obs IDs + short titles; main sections stay outcome-first.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"In this skill the search pass is the ","type":"text"},{"text":"review step","type":"text","marks":[{"type":"strong"}]},{"text":" (see Recipe step 4): the pipeline drafts categories and failure types from keywords (","type":"text"},{"text":"label_source: keyword","type":"text","marks":[{"type":"code_inline"}]},{"text":"); the orchestrator confirms or changes each line item's ","type":"text"},{"text":"category","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"failure_type","type":"text","marks":[{"type":"code_inline"}]},{"text":" from its cited evidence IDs and applies the result with ","type":"text"},{"text":"review --apply","type":"text","marks":[{"type":"code_inline"}]},{"text":". Items left unreviewed keep the \"draft label\" mark and the footer counts them.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Work categories (ALWAYS)","type":"text"}]},{"type":"paragraph","content":[{"text":"Feature","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Bug fix","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Incident","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Maintenance","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Investigation","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Experiment","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Failure / waste types (ALWAYS)","type":"text"}]},{"type":"paragraph","content":[{"text":"Looping","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Hedging","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Wrong turn","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Rework","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Regression","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Premature completion","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Unauthorized action","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Suboptimal path","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Duplicate work","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Blocked work","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Missed requirement","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Unnecessary escalation","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Context re-read","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Model thrash","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Fan-out waste","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ","type":"text"},{"text":"Recovery after miss","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Rework lock (ALWAYS):","type":"text","marks":[{"type":"strong"}]},{"text":" Rework lives ","type":"text"},{"text":"only","type":"text","marks":[{"type":"strong"}]},{"text":" under ","type":"text"},{"text":"failure_type","type":"text","marks":[{"type":"code_inline"}]},{"text":" — never as a work category. Keep category as the intended job type; set ","type":"text"},{"text":"failure_type: Rework","type":"text","marks":[{"type":"code_inline"}]},{"text":" when rework occurred.","type":"text"}]},{"type":"paragraph","content":[{"text":"A line item can have a work category ","type":"text"},{"text":"and","type":"text","marks":[{"type":"strong"}]},{"text":" a failure_type (e.g. Maintenance + Looping).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cost model","type":"text"}]},{"type":"paragraph","content":[{"text":"Measured tokens come from Claude Code transcripts (","type":"text"},{"text":"~/.claude/projects/**/*.jsonl","type":"text","marks":[{"type":"code_inline"}]},{"text":", assistant replies deduped on ","type":"text"},{"text":"(message.id, requestId)","type":"text","marks":[{"type":"code_inline"}]},{"text":"); Codex transcripts are read the same way. Prices are OpenRouter public list prices per million tokens, fetched at run time and saved with the report.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"agent_cost_i (per reply, micro-dollars) =\n input × price.input + output × price.output\n + cache_write_5m × price.cache_write + cache_write_1h × price.cache_write_1h\n + cache_read × price.cache_read # cache_write_1h = listed rate, else 2 × input\n\nagent_estimated_usd = Σ agent_cost_i over every reply in the window (matched or not) # ESTIMATED, the headline\nextrapolated_unmeasured = observer tokens of sessions with no transcript here × (measured $ per observer token)\n # EXTRAPOLATED (low confidence); \"all sessions measured\" when nothing remains\nobserver_note_taker_est = note-taker (observer) tokens, deduped per reply, × its input list price\n # priced separately, never agent cost, never in the headline\nmistakes_estimated_usd = Σ agent_cost_i over the same-session union of wasted turns, each turn once # low figure\ncost_per_completed_outcome = Σ attributed $ for status ∈ {shipped, completed} / count(those work items)\nwaste_rate = Σ wasted_cost / Σ attributed $ recovery_share = Σ recovery_cost / Σ attributed $","type":"text"}]},{"type":"paragraph","content":[{"text":"Line items are sessions: ","type":"text"},{"text":"attributed_usd","type":"text","marks":[{"type":"code_inline"}]},{"text":" = estimated (transcript on this box) or extrapolated (no transcript). ","type":"text"},{"text":"wasted_cost","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"recovery_cost","type":"text","marks":[{"type":"code_inline"}]},{"text":" come from the behavior pass (below), one union set for the ribbon, the line items and the mistakes line. The upper bound (redo windows plus project-wide fallback) stays in Details.","type":"text"}]},{"type":"paragraph","content":[{"text":"Unauthorized blocked:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"direct_cost","type":"text","marks":[{"type":"code_inline"}]},{"text":" $0, ","type":"text"},{"text":"risk_exposure","type":"text","marks":[{"type":"code_inline"}]},{"text":" high, ","type":"text"},{"text":"action_status","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocked. Always keep risk_exposure non-dollar unless real cash/remediation is at stake — never invent risk dollars.","type":"text"}]},{"type":"paragraph","content":[{"text":"confidence","type":"text","marks":[{"type":"code_inline"}]},{"text":" — ","type":"text"},{"text":"high","type":"text","marks":[{"type":"code_inline"}]},{"text":" when tokens + model + outcome are clear; ","type":"text"},{"text":"medium","type":"text","marks":[{"type":"code_inline"}]},{"text":" when allocation across obs is judgmental; ","type":"text"},{"text":"low","type":"text","marks":[{"type":"code_inline"}]},{"text":" when evidence is thin.","type":"text"}]},{"type":"paragraph","content":[{"text":"Unpriced models (not in the price list, or a negative \"variable\" price) are listed by name with their tokens and add nothing; they are never priced at zero silently.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Behavior metrics (heuristic until reviewed)","type":"text"}]},{"type":"paragraph","content":[{"text":"A second pass over the same transcripts tags every user turn human / bot / unknown (relayed agent prompts are never Alex's words), finds frustration episodes, and runs the pattern detectors from the Frustration Arc study: invented human gates, broke working things, wrong or expensive model, over-engineering, did something not asked, fake output, false \"done\", wrong tool or contact, bad outbound (incidents × recipients, never dollars), memory or rule loss, jargon, unclear cause, plus tool errors and hedging (Alex's definition: a caveat given when the answer was already available). Four summary tiles; everything else in Details. Every count is labeled ","type":"text"},{"text":"heuristic","type":"text","marks":[{"type":"code_inline"}]},{"text":" until reviewed or classified. The optional classifier (","type":"text"},{"text":"--classify","type":"text","marks":[{"type":"code_inline"}]},{"text":") is off by default, capped at $2.00 per run, uses only a regular inference ","type":"text"},{"text":"OPENROUTER_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":", and its spend is shown separately.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Money labeling (ALWAYS)","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Label","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Meaning","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Measured","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Provider-reported spend from a sanctioned source (today: the OpenRouter per-key snapshot, shown with its UTC bucket label)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Estimated","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Measured transcript tokens × OpenRouter list price per MTok (the formula above)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Extrapolated","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sessions with no transcript on this machine, from the observer-token ratio; always \"low confidence\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unavailable","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No measured value — write ","type":"text"},{"text":"measured spend unavailable","type":"text","marks":[{"type":"code_inline"}]},{"text":", never ","type":"text"},{"text":"$0 spent","type":"text","marks":[{"type":"code_inline"}]},{"text":" for unknown","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"risk_exposure","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Severity / qualitative unless real cash is at stake (seats, refunds, SLA)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"cost_basis","type":"text","marks":[{"type":"code_inline"}]},{"text":" on each line item: ","type":"text"},{"text":"estimated_usage","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"measured_provider","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"extrapolated","type":"text","marks":[{"type":"code_inline"}]},{"text":". Dollars are shown to two decimals everywhere (","type":"text"},{"text":"$109.25","type":"text","marks":[{"type":"code_inline"}]},{"text":"); every figure carries its label and basis.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Line-item schema","type":"text"}]},{"type":"paragraph","content":[{"text":"Each row in ","type":"text"},{"text":"line-items.csv","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"report.json.line_items","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Field","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"work_item_id","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stable id (","type":"text"},{"text":"WI-1","type":"text","marks":[{"type":"code_inline"}]},{"text":" …)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"title","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Human work-item title (ALWAYS invent at review; drafts use the session's first prompt)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scope","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"project","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"worktree","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Period, session or project; primary project; worktree when relevant","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"session_ids","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"content_session_id","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mem session id(s) and the transcript join key","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"status","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"shipped","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"completed","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"in_progress","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"abandoned","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"blocked","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"category","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"work_category","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"One of the six work categories","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"failure_type","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"failure_signals","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Empty or failure types (Rework lives here)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cost_measured","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Number or ","type":"text"},{"text":"unavailable","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cost_estimated","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"cost_extrapolated","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"attributed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"List-price estimate; extrapolation for sessions with no transcript; the one used","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wasted_cost","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"recovery_cost","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"productive_cost","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"USD from the behavior union (0 if none)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"risk_exposure","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"none","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"low","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"medium","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"high","type":"text","marks":[{"type":"code_inline"}]},{"text":" (non-dollar unless real cash)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"evidence_ids","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"summary_ids","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Observation and summary IDs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"agent_tokens","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{input, output, cache_write_5m, cache_write_1h, cache_read}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"observer_tokens","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Note-taker tokens, Details only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"model","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"model_prices_usd_per_mtok","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dominant model and the prices used","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cost_basis","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"estimated_usage","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"measured_provider","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"extrapolated","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"label_source","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"keyword","type":"text","marks":[{"type":"code_inline"}]},{"text":" (draft) / ","type":"text"},{"text":"llm","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"human","type":"text","marks":[{"type":"code_inline"}]},{"text":", with ","type":"text"},{"text":"reviewed_by","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"reviewed_at_pt","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"device","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"local","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"remote-N","type":"text","marks":[{"type":"code_inline"}]},{"text":", or an export label such as ","type":"text"},{"text":"mac","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"behavior_counts","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pattern hits in this session","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"recommended_action","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"confidence","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"date_pt","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"notes","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"One lever; high/medium/low; PT day; one plain sentence","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deliverables (ALWAYS)","type":"text"}]},{"type":"paragraph","content":[{"text":"acr.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" writes one directory containing:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"report.html","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — self-contained (inline CSS, no script, no external resources), Timing-style","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"report.pdf","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — from ","type":"text"},{"text":"report.print.html","type":"text","marks":[{"type":"code_inline"}]},{"text":" with headless Chrome (","type":"text"},{"text":"google-chrome","type":"text","marks":[{"type":"code_inline"}]},{"text":" on PATH, else Chrome's standard install location on macOS and Windows); when Chrome is missing the run says \"PDF skipped, HTML is canonical\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"report.json","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — window, scope, spend, totals, by_day, by_model, by_device, line_items, wins, behavior, timeline","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"line-items.csv","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — one row per work item","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"evidence.json","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — observation IDs cited, short titles, observer tokens, model, session ids","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"labels.review.json","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — the drafted labels for the review pass","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Manager-facing HTML sections (required order)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hero","type":"text","marks":[{"type":"strong"}]},{"text":" — dollars with its tag (ESTIMATE / MEASURED), the basis sentence, \"N things finished, about $X each\", the extrapolated line, \"Measured provider spend: unavailable\" when it is, and the story sentence (computed, never hand-written)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Wins vs mistakes","type":"text","marks":[{"type":"strong"}]},{"text":" — the cost-of-mistakes line (low figure), wins shipped (merged PRs, published versions, praise; cost \"unmeasured\" until sessions are linked to PRs), and two timelines on one PT-day axis","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cost ribbon","type":"text","marks":[{"type":"strong"}]},{"text":" — every piece of work as a block sized by cost, waste hatched, in-progress striped","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Where the money went","type":"text","marks":[{"type":"strong"}]},{"text":" (donut by kind of work) · ","type":"text"},{"text":"Day by day","type":"text","marks":[{"type":"strong"}]},{"text":" (stacked bars, empty days say \"no agent work\") · ","type":"text"},{"text":"How much was useful","type":"text","marks":[{"type":"strong"}]},{"text":" (the ring, the behavior strip with at most four tiles)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"What got done","type":"text","marks":[{"type":"strong"}]},{"text":" — one folded row per work item, most expensive first, draft labels marked","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Worth your attention","type":"text","marks":[{"type":"strong"}]},{"text":" — at most three, computed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Details & evidence","type":"text","marks":[{"type":"strong"}]},{"text":" (folded; open in the PDF) — wins, mistakes by day, behavior pattern table with the upper bound, rule effectiveness, spend and pricing, models, failure accounting, line items, devices, unmatched transcripts, unpriced models, label counts, the honesty rules, links to the CSV and JSON","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Truthfulness ALWAYS rules","type":"text"}]},{"type":"paragraph","content":[{"text":"Positive truth rules only:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always label estimate vs measured (","type":"text"},{"text":"cost_basis","type":"text","marks":[{"type":"code_inline"}]},{"text":" = ","type":"text"},{"text":"estimated_usage","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"measured_provider","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always say ","type":"text"},{"text":"measured spend unavailable","type":"text","marks":[{"type":"strong"}]},{"text":" when unmatched — never \"$0 spent\" for unknown.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always use ","type":"text"},{"text":"completed outcomes","type":"text","marks":[{"type":"strong"}]},{"text":" as the unit (cost per completed outcome).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always keep ","type":"text"},{"text":"risk_exposure","type":"text","marks":[{"type":"code_inline"}]},{"text":" non-dollar unless real cash/remediation is at stake.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always invent human work-item titles; observation titles are evidence-only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always progressive Mem Search; evidence IDs live in the appendix.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always put Rework under ","type":"text"},{"text":"failure_type","type":"text","marks":[{"type":"code_inline"}]},{"text":" only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always default scope to most recent sessions unless the user names another.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Added by the rebuild, same spirit: the note-taker's cost is separate and never in the headline; keyword labels are drafts until reviewed; Grok Bot and Mac figures are \"unavailable\" or \"extrapolated (low confidence)\", never $0 and never guessed; the live database is only ever opened read-only (every run works on a snapshot).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Recipe (orchestrator)","type":"text"}]},{"type":"paragraph","content":[{"text":"All commands from the skill directory; ","type":"text"},{"text":"OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" is one run directory (for example ","type":"text"},{"text":"/tmp/acr-weekly/run","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Every step is idempotent.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prices","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"python3 scripts/acr.py prices --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" (OpenRouter list prices; ","type":"text"},{"text":"--prices \u003csaved prices.json>","type":"text","marks":[{"type":"code_inline"}]},{"text":" to run offline; no key needed).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Collect","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"python3 scripts/acr.py collect --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default window) or ","type":"text"},{"text":"… --start YYYY-MM-DD --end YYYY-MM-DD","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"… --session \u003cid>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"… --project \u003cname> --start … --end …","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rollup","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"python3 scripts/acr.py rollup --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the same period flags. Writes report.json, line-items.csv, evidence.json, labels.review.json. Options: ","type":"text"},{"text":"--no-gh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (skip the read-only ","type":"text"},{"text":"gh pr view","type":"text","marks":[{"type":"code_inline"}]},{"text":" confirmation of merged PRs), ","type":"text"},{"text":"--classify","type":"text","marks":[{"type":"code_inline"}]},{"text":" (see above), ","type":"text"},{"text":"--rules-dir \u003chouse rules>","type":"text","marks":[{"type":"code_inline"}]},{"text":" (rule effectiveness).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Review","type":"text","marks":[{"type":"strong"}]},{"text":" — for each line item in ","type":"text"},{"text":"labels.review.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"get_observations","type":"text","marks":[{"type":"code_inline"}]},{"text":" on its ","type":"text"},{"text":"evidence_ids","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not whole timelines), confirm or change ","type":"text"},{"text":"category","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"failure_type","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"status","type":"text","marks":[{"type":"code_inline"}]},{"text":", and the title; write the reviewed file with ","type":"text"},{"text":"reviewed_by","type":"text","marks":[{"type":"code_inline"}]},{"text":" (model id or ","type":"text"},{"text":"Alex","type":"text","marks":[{"type":"code_inline"}]},{"text":"); apply with ","type":"text"},{"text":"python3 scripts/acr.py review --apply \u003creviewed.json> --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":". Items left unreviewed stay marked \"draft label\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Render","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"python3 scripts/acr.py render --in $OUT/report.json --out $OUT --print","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PDF","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"python3 scripts/acr.py pdf --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" (skipped with a message when Chrome is missing).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optional, needs Alex's go — measured OpenRouter spend","type":"text","marks":[{"type":"strong"}]},{"text":": with a regular inference key in the environment, ","type":"text"},{"text":"OPENROUTER_API_KEY=… python3 scripts/acr.py measure-openrouter --out $OUT","type":"text","marks":[{"type":"code_inline"}]},{"text":" before step 3. Without the key the file says ","type":"text"},{"text":"unavailable","type":"text","marks":[{"type":"code_inline"}]},{"text":" and nothing else changes.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optional, needs Alex's go — Mac export merge","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"python3 scripts/acr.py rollup … --device-usage \u003cdevice-usage-mac.json>","type":"text","marks":[{"type":"code_inline"}]},{"text":" (see Gaps and gates).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Return absolute paths, the skill slug ","type":"text"},{"text":"agent-cost-report","type":"text","marks":[{"type":"code_inline"}]},{"text":", the headline dollars with their label, the mistakes line, the wins, PDF yes/no, and how the report answers the five product questions.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Gaps and gates","type":"text"}]},{"type":"paragraph","content":[{"text":"Each of these needs Alex's explicit go; the report shows the honest fallback until then.","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OpenRouter key (needs Alex)","type":"text","marks":[{"type":"strong"}]},{"text":" — a regular inference ","type":"text"},{"text":"OPENROUTER_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":" supplied as an environment variable through the house's secure secret flow; never a management or provisioning key, never read from a settings file, never printed or written to any output. The per-key endpoint gives UTC day/week/month snapshots, so the figure is shown with its bucket label and switches the hero to MEASURED only when the bucket fully covers the report window. A range-by-day source (","type":"text"},{"text":"/api/v1/activity","type":"text","marks":[{"type":"code_inline"}]},{"text":") exists but needs a management key, so it is not implemented (G5).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mac transcripts (needs Alex)","type":"text","marks":[{"type":"strong"}]},{"text":" — sessions from Alex's Mac have no transcript on the box, so their cost is EXTRAPOLATED (low confidence) until a device export is merged. Either Alex runs, on the Mac, from a plain checkout of this skill (system ","type":"text"},{"text":"python3","type":"text","marks":[{"type":"code_inline"}]},{"text":" 3.9+): ","type":"text"},{"text":"python3 scripts/acr.py collect --export-device mac --start YYYY-MM-DD --end YYYY-MM-DD --out ~/acr-export","type":"text","marks":[{"type":"code_inline"}]},{"text":" and shares ","type":"text"},{"text":"~/acr-export/device-usage-mac.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (ids, timestamps, token counts, model names; no prompt or observation text, no settings, no paths); or, only after Alex's explicit go for that specific run (window, machine, destination named), an orchestrating agent runs the same command on the registered Mac through the house's registered-machine tooling and copies only ","type":"text"},{"text":"device-usage-mac.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" to the box. A go for one run is not a go for the next (G6 covered Sep 18–26 only). Merge with ","type":"text"},{"text":"--device-usage","type":"text","marks":[{"type":"code_inline"}]},{"text":"; joined sessions flip to ","type":"text"},{"text":"estimated_usage","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"device: mac","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Grok Bot (ALWAYS unavailable, no seat count)","type":"text","marks":[{"type":"strong"}]},{"text":" — Grok Bot is Cursor's cloud agent. Cursor shows its weekly usage only on the plan screen; there is no API or export, and the house has no xAI key for it. The report says exactly \"Grok Bot usage: unavailable\" (G4): no dollars, no guessed per-seat price, no seat count. A figure Alex supplies by hand would be entered as ","type":"text"},{"text":"measured_manual","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"entered_by: Alex","type":"text","marks":[{"type":"code_inline"}]},{"text":" and the date, never inferred; that would be a new decision.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Wins from GitHub (read-only)","type":"text","marks":[{"type":"strong"}]},{"text":" — besides box transcripts and ship observations, ","type":"text"},{"text":"rollup","type":"text","marks":[{"type":"code_inline"}]},{"text":" lists merged PRs with ","type":"text"},{"text":"gh pr list --state merged","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the repos in ","type":"text"},{"text":"--wins-repo","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default ","type":"text"},{"text":"thedotmack/claude-mem","type":"text","marks":[{"type":"code_inline"}]},{"text":"), filtered by merge time inside the PT window and deduplicated by repo and PR number. When ","type":"text"},{"text":"gh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing, unauthenticated or rate-limited, that source reads \"unavailable\" in Details and the report still completes; it is never a zero. ","type":"text"},{"text":"--no-gh","type":"text","marks":[{"type":"code_inline"}]},{"text":" turns every gh call off.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Win cost (unmeasured until R4)","type":"text","marks":[{"type":"strong"}]},{"text":" — sessions are not linked to PRs today, so every win reads \"unmeasured (session not linked to PR)\". Once commits carry a ","type":"text"},{"text":"Claude-Session: \u003ccontent_session_id>","type":"text","marks":[{"type":"code_inline"}]},{"text":" trailer (G13: trailer only, bare id), PR and publish wins get ","type":"text"},{"text":"ESTIMATED · session-linked","type":"text","marks":[{"type":"code_inline"}]},{"text":" costs. There is no fallback estimate (G11).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Merge to main, npm publish, ","type":"text","marks":[{"type":"strong"}]},{"text":"/version-bump","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — each needs Alex's separate go. Pushing the work branch and opening the PR are routine.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Keeping copies in sync","type":"text"}]},{"type":"paragraph","content":[{"text":"The plugin directory is the source of truth. ","type":"text"},{"text":"python3 scripts/acr.py sync-check","type":"text","marks":[{"type":"code_inline"}]},{"text":" compares ","type":"text"},{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"CHECKSUMS.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"scripts/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" against the house copy (","type":"text"},{"text":"~/agent-data/workflows/agent-cost-report/","type":"text","marks":[{"type":"code_inline"}]},{"text":") and the four mirror plugins (","type":"text"},{"text":"claude-mem-cursor","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"claude-mem-grok-bot","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cowork","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"openclaw","type":"text","marks":[{"type":"code_inline"}]},{"text":", each under ","type":"text"},{"text":"skills/agent-cost-report/","type":"text","marks":[{"type":"code_inline"}]},{"text":") and exits 1 on drift; ","type":"text"},{"text":"--write","type":"text","marks":[{"type":"code_inline"}]},{"text":" copies plugin → destinations and re-checks (G2). ","type":"text"},{"text":"CHECKSUMS.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"sha256sum -c","type":"text","marks":[{"type":"code_inline"}]},{"text":" compatible.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Related","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"claude-mem ","type":"text"},{"text":"mem-search","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill — progressive recall","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"plans/2026-09-25-agent-cost-report-weekly.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" — the rebuild plan, decisions G1–G16, and the verification targets","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Creed","type":"text"}]},{"type":"paragraph","content":[{"text":"Claude-Mem skill · outcomes first · estimates labeled · most recent by default · rework under failure_type · progressive recall · HTML is canonical · humans see what the agent did and why it cost money.","type":"text","marks":[{"type":"strong"}]}]}]},"metadata":{"date":"2026-11-02","name":"Agent Cost Report","author":"@skillopedia","source":{"stars":110501,"repo_name":"claude-mem","origin_url":"https://github.com/thedotmack/claude-mem/blob/HEAD/cowork/skills/agent-cost-report/SKILL.md","repo_owner":"thedotmack","body_sha256":"40b6e0045dbe7343d649bfddc313afc7f11f3ee5b44036ddebc73832635f0a8c","cluster_key":"dafffd253ba78c69943530ed196a24dd86cf31c270dda24a0d85a47bd6fcd1fb","clean_bundle":{"format":"clean-skill-bundle-v1","source":"thedotmack/claude-mem/cowork/skills/agent-cost-report/SKILL.md","text_bytes":528603,"source_commit":"63efe315b4bb85b21dad871cf7e6a9dbd1867864","skills_sh_rank":19496,"attachment_count":47,"text_attachments":47,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"cowork/skills/agent-cost-report/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"ai-agent-development","category_label":"AI"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"ai-agent-development","import_tag":"skills-sh-top20k-2026-11","description":"Believable agent cost report for any period, default the last 7 full days PT, not counting today. Measured tokens from Claude Code transcripts priced at OpenRouter list prices (ESTIMATED), measured provider spend when a sanctioned source exists, note-taker cost separate, Timing-style HTML/PDF plus report.json, line-items.csv, evidence.json.","allowed-tools":["Bash","Read","Write","AskUserQuestion","mcp__plugin_claude-mem_mcp-search__search","mcp__plugin_claude-mem_mcp-search__timeline","mcp__plugin_claude-mem_mcp-search__get_observations"]}},"renderedAt":1793671602882}

Agent Cost Report Claude-Mem / Claude Code skill. Runtime is the pipeline (transcripts → tokens → dollars → Timing-style report) plus a progressive Mem Search review pass that confirms the drafted labels. The Notion draft is SPEC history only — never the product, never the runtime, never the ship vehicle. Resolve the absolute directory containing this ; all helper paths are relative to that directory. is the shortcut: . Python 3.9+ standard library only, with IANA timezone data for ; no pip installs. is the only exception to the no-pip-installs rule, and only when exits saying that timezone d…