You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- fix(tui): arrow keys on an empty table hard-locked the tview event loop
at 100% CPU (Table's selectable scan never terminates with zero selectable
rows); row selection now follows data-row presence, regression-tested
- feat(aade): credentials read from YOURDATA_USERNAME / YOURDATA_SUBSCRIPTION_KEY;
the binary refuses to start in every mode without both, naming the missing vars
- feat: endpoint defaults to production; --dev forces the AADE test endpoint,
YOURDATA_ENDPOINT is an explicit override (--dev wins)
- build: make build emits bin/yourdata; tcell pinned to tview's declared v2.8.1
- docs: SPECIFICATIONS.md, README.md, CLAUDE.md, tui-rules.md updated to match
Copy file name to clipboardExpand all lines: docs/SPECIFICATIONS.md
+20-9Lines changed: 20 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,6 +32,16 @@ only protocol frames). The database file resolves as `-db` flag > `$YOURDATA_DB`
32
32
`$XDG_DATA_HOME/yourdata/yourdata.db` (defaulting to `~/.local/share/yourdata/yourdata.db`); the
33
33
directory is created on first run. Both surfaces may run **simultaneously** against the same file.
34
34
35
+
**Startup requirement.** The binary refuses to start — in **every** mode — unless both
36
+
`YOURDATA_USERNAME` and `YOURDATA_SUBSCRIPTION_KEY` are set, exiting immediately with an error that
37
+
names each missing variable. Credentials are read from the environment only and are never persisted.
38
+
39
+
**Endpoint selection.** The AADE endpoint defaults to **production**. It resolves as: `--dev` flag
40
+
(forces the AADE development/test endpoint, overriding everything) > `YOURDATA_ENDPOINT` (explicit
41
+
endpoint override) > production. Both surfaces honour the same resolution, and the PROD safeguards
42
+
remain regardless of how production was selected: the TUI's typed-`SUBMIT` confirmation and the MCP
43
+
`submit_invoice` refusal on production.
44
+
35
45
---
36
46
37
47
## Goals & Non-Goals
@@ -72,8 +82,9 @@ Greek tax authority's REST API. That dependency is contained as follows, and no
72
82
pricing, previewing XML and reading history all work with the network down.
73
83
- A network failure surfaces as a `[red]` message in the status bar (TUI) or a tool-level error
74
84
result (MCP). It never crashes the app and never corrupts local state.
75
-
- Missing credentials are **not** a startup failure. The app runs normally and refuses only those
76
-
two actions, with an explanation.
85
+
- Missing credentials **are a startup failure**: the binary exits with an error naming the missing
86
+
variables (see the startup requirement above). As defence-in-depth, the two network actions also
87
+
refuse with an explanation if they are ever reached without credentials.
77
88
78
89
Also out of scope for v1: multiple issuer entities, multi-branch series scoping, invoice PDF
79
90
rendering, e-mailing invoices, payment tracking, and any AADE endpoint the SDK does not implement
@@ -93,7 +104,7 @@ product and are not defects to be worked around:
93
104
| There is **no cancel/void endpoint**. | Submission is irreversible. Correction is only possible via a credit note (`5.1`/`5.2`) carrying `correlatedInvoices`. This drives the immutability rule and the submit confirmation. |
94
105
|`InvoiceSummaryBuilder.Build()` hardcodes `TotalWithheldAmount`, `TotalStampDutyAmount` and `TotalDeductionsAmount` to `0`, and never populates `ExpensesClassifications` (deliberate TS parity — `summary.go:16-21`). |**We do not call `CalculateSummary()`.**`internal/models` computes all eight totals and both classification groupings itself. |
95
106
|`RequestArgs` exposes `NextPartitionKey`/`NextRowKey` but the SDK does not loop — and its response envelope surfaces a continuation only via `BookInfo.ContinuationToken` (a doc-level token element is discarded). | The fetch use-case follows continuation tokens itself, bounded. A token it cannot split into partition/row keys ends the loop with the snapshot stored and flagged `Partial=true` — never silently truncated. |
96
-
|`LoadConfig()` reads `AADE_USER_ID`, `AADE_SUB_KEY`, `AADE_ENDPOINT`from the environment. | Credentials are environment-only and never persisted. |
107
+
|The SDK's own `LoadConfig()` reads `AADE_*` variables — this app does **not** use it. It builds `sdk.Config` itself from `YOURDATA_USERNAME`, `YOURDATA_SUBSCRIPTION_KEY`, `YOURDATA_ENDPOINT`. | Credentials are environment-only, app-prefixed, and never persisted. |
97
108
|`sdk.ResponseDoc.InvoiceMark` is `*int64` (~15 digits). | Stored as `int64`; **serialised as a decimal string** in all MCP output, so JS-based MCP clients cannot lose precision. |
98
109
99
110
The SDK is used for what it is good at: AADE enum constants, XML marshalling (`MarshalInvoicesDoc`),
@@ -260,7 +271,7 @@ grouped too**.
260
271
| Field | Type | Notes |
261
272
|---|---|---|
262
273
|`SubmittedAt`|`time.Time`||
263
-
|`Endpoint`|`string`| The `AADE_ENDPOINT` in force — records test vs. prod. |
274
+
|`Endpoint`|`string`| The resolved endpoint in force — records test vs. prod. |
264
275
|`StatusCode`|`string`| From `sdk.ResponseDoc`. |
265
276
|`Mark`|`int64`| AADE MARK. **Serialised as a decimal string in MCP output.**|
266
277
|`UID`|`string`|`invoiceUid`. |
@@ -390,7 +401,7 @@ counterparty names are resolved within the transaction so an invoice stays finda
390
401
1.**Validate locally.** Reject if: no lines; `Series` or `AA` blank; no `InvoiceType`; no
391
402
counterparty; a line has `VATCategory == "7"` without a `VATExemptionCategory`; `Currency != EUR`
392
403
without an `ExchangeRate`; or the status is already `submitted`.
393
-
2.**Check credentials.** Missing `AADE_USER_ID`/`AADE_SUB_KEY` → refuse with an explanatory
404
+
2.**Check credentials.** Missing `YOURDATA_USERNAME`/`YOURDATA_SUBSCRIPTION_KEY` → refuse with an explanatory
394
405
message. No network call attempted.
395
406
3.**Freeze and marshal.** Resolve the counterparty into a `Party` copy, recompute the summary, map
396
407
to `sdk.Invoice`, `sdk.MarshalInvoicesDoc`.
@@ -473,7 +484,7 @@ including `create_invoice_draft` and every other draft-building, reference-data
473
484
Exactly one tool is gated:
474
485
475
486
-**`submit_invoice`** is registered **only** when `YOURDATA_MCP_ALLOW_SUBMIT=1`, and even then it
476
-
**refuses at call time** when `AADE_ENDPOINT` resolves to production (`sdk.EnvProd`). An agent may
487
+
**refuses at call time** when the resolved endpoint is production (`sdk.EnvProd`). An agent may
477
488
rehearse a full submission against the AADE dev sandbox; filing a real tax document remains a
478
489
human act at the TUI.
479
490
@@ -644,7 +655,7 @@ check runs on field change and renders a **non-blocking `[yellow]` warning** —
644
655
645
656
**Submit confirmation.** A centered `Modal` over the body, **focus defaulting to `No`**, naming the
646
657
series/AA, the counterparty, the gross total and the target environment, and stating that
647
-
**submission cannot be undone**. When `AADE_ENDPOINT` is production, the modal additionally requires
658
+
**submission cannot be undone**. When the resolved endpoint is production, the modal additionally requires
648
659
typing `SUBMIT` before `Yes` is enabled — `tui-rules.md` reserves this for high-blast-radius actions,
649
660
and filing a real tax document qualifies. During the call the status bar shows a spinner; the outcome
650
661
lands there as `[green]✓ MARK 400001234567890` or `[red]✗ <error code> <message>`.
0 commit comments