cctally 1.98.0 → 1.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2611,12 +2611,18 @@ def _build_account_parser(subparsers, name, *, help_text, xref=None):
2611
2611
  show <ref> Show one account's identity + attribution summary.
2612
2612
  label <ref> <name>
2613
2613
  Set a durable user label (survives db rebuild).
2614
+ attribute <ref> --since <iso> [--until <iso>]
2615
+ Attribute already-recorded Codex quota windows
2616
+ and spend to a known Codex account. Previews by
2617
+ default; --yes applies; --retract withdraws.
2614
2618
 
2615
2619
  Examples:
2616
2620
  cctally account list
2617
2621
  cctally account list --json
2618
2622
  cctally account show work@example.com
2619
2623
  cctally account label 3f2a1b work
2624
+ cctally account attribute work --since 2025-12-01T00:00:00Z
2625
+ cctally account attribute work --since 2025-12-01T00:00:00Z --yes
2620
2626
  """),
2621
2627
  )
2622
2628
  acct_sub = acct_p.add_subparsers(dest="account_action", required=True)
@@ -2633,6 +2639,65 @@ def _build_account_parser(subparsers, name, *, help_text, xref=None):
2633
2639
  acct_label.add_argument("ref", help="Account ref (label / email / key prefix)")
2634
2640
  acct_label.add_argument("label", help="New label")
2635
2641
  acct_label.set_defaults(func=c.cmd_account)
2642
+ acct_attr = acct_sub.add_parser(
2643
+ "attribute",
2644
+ help="Attribute recorded Codex windows and spend to an account",
2645
+ formatter_class=CLIHelpFormatter,
2646
+ description=textwrap.dedent("""\
2647
+ Attribute already-recorded Codex quota windows and spend to a known
2648
+ Codex account (#500).
2649
+
2650
+ Codex only. A Claude ref is rejected, and so is the literal
2651
+ 'unattributed' — attributing data TO the sentinel is not a fact an
2652
+ operator can assert.
2653
+
2654
+ --since is required and --until optional; both are timezone-aware ISO
2655
+ instants normalized to UTC, and the range is half-open [since, until).
2656
+ A naive value is a usage error. An omitted --until means "up to now".
2657
+
2658
+ Attribution is asserted per WHOLE physical window group. A group the
2659
+ range covers only partially is refused rather than split; widen the
2660
+ range and rerun.
2661
+
2662
+ Preview is the default and writes nothing. --yes applies, all or
2663
+ nothing over the groups the range could have meant: a partial_group,
2664
+ native_account_conflict, assertion_conflict or spend_account_conflict
2665
+ refusal stops the whole run and nothing is written. A group that is
2666
+ not account weekly quota (not_weekly, model_scoped) is reported as
2667
+ refused and left alone WITHOUT blocking the rest, because a time
2668
+ range selected it rather than you naming it.
2669
+
2670
+ --retract withdraws assertions instead, selecting over the durable
2671
+ assertion records rather than over observations, so a dormant or split
2672
+ assertion can still be tombstoned.
2673
+
2674
+ Exit codes: 0 preview / empty selection / no-op / successful apply;
2675
+ 2 every validation failure and every refusal; 3 operational failure.
2676
+
2677
+ Examples:
2678
+ cctally account attribute work --since 2025-12-01T00:00:00Z
2679
+ cctally account attribute work --since 2025-12-01T00:00:00Z \\
2680
+ --until 2026-07-01T00:00:00Z --yes
2681
+ cctally account attribute work --since 2025-12-01T00:00:00Z \\
2682
+ --retract --yes
2683
+ """),
2684
+ )
2685
+ acct_attr.add_argument("ref", help="Codex account ref (label / email / key prefix)")
2686
+ acct_attr.add_argument(
2687
+ "--since", dest="since", required=True,
2688
+ help="Inclusive start, timezone-aware ISO instant (required).")
2689
+ acct_attr.add_argument(
2690
+ "--until", dest="until",
2691
+ help="Exclusive end, timezone-aware ISO instant (default: now).")
2692
+ acct_attr.add_argument(
2693
+ "--retract", dest="retract", action="store_true",
2694
+ help="Withdraw matching assertions instead of recording one.")
2695
+ acct_attr.add_argument(
2696
+ "--yes", dest="yes", action="store_true",
2697
+ help="Apply the plan (default is a preview that writes nothing).")
2698
+ acct_attr.add_argument("--json", dest="emit_json", action="store_true",
2699
+ help="Emit the stamped-first camelCase JSON envelope.")
2700
+ acct_attr.set_defaults(func=c.cmd_account)
2636
2701
 
2637
2702
 
2638
2703
  def _build_alerts_parser(subparsers, name, *, help_text, xref=None):