summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-07-02 09:07:10 +0200
committerDanilo M. <danix@danix.xyz>2026-07-02 09:07:10 +0200
commit1802450e437de9d31b9044089c162993866f023d (patch)
tree467966efc0f49da19870dcc5c9dc40ab556d2cfc
parent228097e18fdffdc3d8b34d3017dad14267b9acaa (diff)
downloadfirefly-cli-1802450e437de9d31b9044089c162993866f023d.tar.gz
firefly-cli-1802450e437de9d31b9044089c162993866f023d.zip
docs: spec for tx add --from-id/--to-id (ISSUES.md #2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--docs/superpowers/specs/2026-07-02-from-id-to-id-design.md87
1 files changed, 87 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-07-02-from-id-to-id-design.md b/docs/superpowers/specs/2026-07-02-from-id-to-id-design.md
new file mode 100644
index 0000000..59f54b2
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-02-from-id-to-id-design.md
@@ -0,0 +1,87 @@
+# `--from-id`/`--to-id` for `tx add`
+
+Date: 2026-07-02
+Resolves: ISSUES.md #2 (same-name accounts unresolvable via CLI)
+
+## Problem
+
+Two accounts share the name "Nexi": expense (id 52, the 15th debt-payment
+target) and revenue (id 129, the 1st-of-month plafond-refill source).
+`firefly tx add --from/--to "Nexi"` fails with
+`Ambiguous account "Nexi" matches ids 52, 129`. The CLI accepts names only; a
+numeric id is treated as a literal name and also fails. The intended
+two-same-name-account cycle cannot be driven by the CLI.
+
+## Goal
+
+Add an id escape hatch to `tx add` so an ambiguous (or any) account can be
+targeted by numeric id, bypassing name resolution.
+
+## Design
+
+### New resolver method (`resolver.py`)
+
+```python
+def account_by_id(self, acc_id):
+ resp = self.client.request("GET", f"/api/v1/accounts/{acc_id}")
+ item = resp["data"]
+ return {"id": item["id"], **item["attributes"]}
+```
+
+GET a single account by id. Returns the same dict shape as `account(name)`
+(`{"id", "name", "type", ...}`) so all downstream code is path-agnostic. A
+bad id yields a 404 that `client.request` already surfaces as a
+`FireflyError` — this is the existence validation (per the "validate id"
+decision), no extra list scan needed.
+
+### Flags (`transaction.py` `_add_args`)
+
+- Add `--from-id` (dest `source_id`) and `--to-id` (dest `dest_id`).
+- Drop `required=True` from `--from`/`--to`; requirement is now one-of-two
+ per side, enforced in the handler (argparse can't express XOR cleanly).
+
+### Handler validation (`cmd_add`)
+
+Per side, exactly one of the name/id pair must be supplied:
+
+- source: exactly one of `args.source`, `args.source_id`
+- dest: exactly one of `args.dest`, `args.dest_id`
+
+Zero or both on a side → `FireflyError` with a clear message. Sides are
+independent: `--from NAME --to-id 129` is valid (name on one side, id on the
+other).
+
+### Resolution
+
+```python
+src = (ctx.resolver.account_by_id(args.source_id) if args.source_id
+ else ctx.resolver.account(args.source))
+dst = (ctx.resolver.account_by_id(args.dest_id) if args.dest_id
+ else ctx.resolver.account(args.dest))
+```
+
+Everything downstream is unchanged: type inference, the transfer-direction
+stderr echo, the skip-dupes search query, and the emitted split all use
+`src`/`dst` dicts identically for both paths.
+
+## Contract impact → PATCH (v0.3.7)
+
+New optional flags. `--from`/`--to` are no longer argparse-required, but the
+handler still requires one per side, so existing name-only callers are
+unaffected. JSON output shape and exit codes are unchanged. Per the
+contract-keyed scheme this is a PATCH.
+
+## Tests (mocked)
+
+- id path resolves via `account_by_id` and writes the split with that id
+- missing both name and id on a side → error
+- both name and id on the same side → error
+- mixed: name on source, id on dest → works
+- a 404 from `account_by_id` surfaces as a FireflyError
+
+## Out of scope (YAGNI)
+
+- id support on `tx list` or other name-taking commands — add when a real
+ blocker appears; `tx list` was not reported as blocked.
+- `--from-type`/type-scoped resolution — the id flags cover disambiguation.
+- ISSUES.md #1 (liability account creation) — separate, larger, next session.