src/target_revenue/effort_calculator.py implements Candidate A (labor-cost-anchored, accepted T01): commit-timestamp session-gap clustering for human interaction time, workplan/task-volume counts via direct workplans/ directory parsing (no state-hub dependency, works uniformly on any repo using this repo's own convention), file/line counts with generated/vendored-path exclusion, and caller-supplied token-cost pricing. estimate_target_basis() combines these and returns a derivation dict (every input shown) plus a warnings list - never a black-box dollar figure. 1-day manual-work floor, as requested: any raw commit-clustered estimate below 1.0 day is floored and flagged with a warning that this is very likely a measurement gap (commit-clustering is a floor estimate by design) that should usually be compensated for by manual override, not trusted at face value. A second, independent sanity-check warning fires when finished-workplan/task volume is substantial but the time estimate is still low, even above the floor - demonstrated live against target-revenue's own history (7 finished workplans, 57 tasks correctly flagged a 2.38-day estimate as under-counted). scripts/effort_calculator_cli.py: CLI wrapper printing JSON, following the same offline-first, no-Phase-declaration pattern as scripts/trf_onboard.py. tests/test_effort_calculator.py (15 deterministic tests, throwaway git repos/tmp_path fixtures) covers commit clustering, workplan/task parsing, size-metric exclusion, token-cost pricing, the floor-and-warning behavior, the sanity-check warning, and an end-to-end smoke test. No new hard dependency.
57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""CLI wrapper for target_revenue.effort_calculator (WP-0010-T02).
|
|
|
|
Prints a proposed `phase.target_basis` estimate for a repo, with full
|
|
derivation and warnings, as JSON. Token counts are caller-supplied (e.g.
|
|
pulled from the state hub's `get_token_summary` beforehand) rather than
|
|
fetched here — this script has no MCP client of its own, matching the
|
|
offline-first design of the rest of this library.
|
|
|
|
Does not select a Target Multiple and does not register anything with
|
|
the hosted Trust Service — this is an estimation aid, not a Phase
|
|
declaration tool (that remains `scripts/trf_onboard.py`).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import dataclasses
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT / "src"))
|
|
|
|
from target_revenue import effort_calculator as ec # noqa: E402
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("repo_path", help="path to the repo to estimate effort for")
|
|
parser.add_argument("--daily-rate", type=float, required=True)
|
|
parser.add_argument("--tokens-in", type=int, default=0)
|
|
parser.add_argument("--tokens-out", type=int, default=0)
|
|
parser.add_argument("--model", default="default")
|
|
parser.add_argument("--session-gap-hours", type=float, default=ec.DEFAULT_SESSION_GAP_HOURS)
|
|
parser.add_argument("--hours-per-day", type=float, default=ec.DEFAULT_HOURS_PER_DAY)
|
|
args = parser.parse_args(argv)
|
|
|
|
result = ec.calculate_target_basis(
|
|
args.repo_path,
|
|
daily_rate=args.daily_rate,
|
|
tokens_in=args.tokens_in,
|
|
tokens_out=args.tokens_out,
|
|
model=args.model,
|
|
session_gap_hours=args.session_gap_hours,
|
|
hours_per_day=args.hours_per_day,
|
|
)
|
|
|
|
output = dataclasses.asdict(result)
|
|
print(json.dumps(output, indent=2))
|
|
if result.warnings:
|
|
print(f"\n{len(result.warnings)} warning(s) — see 'warnings' above.", file=sys.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|