Commit Graph
5 Commits
Author SHA1 Message Date
awkawbandClaude Opus 5 0485272080 Document the new endpoints and the fixture workflow
Updates the three coverage summaries to 325 methods so they agree, moves the
per-resource extras out of the gap list in `COVERAGE.md` now that they are
wrapped, and records why the fixtures exist and how to refresh them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-24 21:56:46 -04:00
awkawbandClaude Opus 5 e4f2373b53 Add multipart document uploads for the 16 resources that have the route
First item on the GOALS.md roadmap. `upload_{x}(id, files, is_public)`
attaches files through `/{resource}/{id}/upload` and returns the
refreshed record. Verified against upstream that all 16 controllers
answer with `itemResponse($x->fresh())`.

Three details that look wrong but are not, each checked in the upstream
source rather than taken from the published spec:

- The request is a POST carrying `_method=PUT`. The route is registered
  PUT, but PHP does not parse a multipart body on a real PUT, so the
  files never arrive. Laravel's kernel calls
  `enableHttpMethodParameterOverride()`, turning the spoofed method back
  into a PUT before routing. The spec documents POST with `_method=POST`,
  which would 405 against these routes.
- `is_public` is sent as 1/0. Laravel's `boolean` rule accepts only
  true, false, 0, 1, '0' and '1' under strict comparison, so "true"
  fails validation.
- Uploads are never retried. An upload adds a document, so it is not
  idempotent whatever its verb, and a replay after a lost response would
  attach the file twice. This falls out of reqwest's multipart body being
  un-cloneable, which nothing in the types enforces, so a test pins it
  for 500, 503 and 429.

An upload with no files, a nameless file or an unparseable MIME type is
refused locally (`Error::InvalidUpload`), since the server answers an
empty upload with a success that changed nothing.

Corrects two earlier claims:

- GOALS.md and COVERAGE.md said uploads would make `create_document`
  possible. They do not: `DocumentController::store` is an empty stub,
  and an upload is the only way a document is created. There is no
  `create_document`.
- The documented method count (265) had been stale since the typed
  `statics`/`search`/`health_check` commit, which added three `_as`
  methods without updating it. Recounted from the source: 284 = 210 +
  18 + 12 + 16 uploads + 28 hand-written.

Also fixes a README example, `let statics: Value = client.statics()`,
that stopped compiling when `statics` lost its type parameter.

`edocument/upload` is left out: it imports an expense from an
e-document rather than attaching a file, so it moves to the
import/export roadmap item.

Enabling `multipart` adds mime, mime_guess and unicase; MSRV stays 1.88,
re-verified by running the suite on it. 101 tests, up from 91.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-24 14:26:41 -04:00
awkawbandClaude Opus 5 355cf08324 Point at the GitHub mirror and document that it is read-only
CI / test (push) Canceled after 0s
CI / Build on the declared MSRV (push) Canceled after 0s
CI / Latest stable (advisory) (push) Canceled after 0s
`Cargo.toml` named github.com/bryant/invoiceninjars, a guess from the
git author name that was never a real repository. It now points at the
mirror that actually exists, so the crates.io "Repository" link will
resolve.

Gitea is the source of truth and a push mirror copies every commit to
GitHub. Anything written to the GitHub copy is overwritten by the next
sync, so a merged pull request there would silently vanish. README and
GOALS.md say so, and direct issues and pull requests to Gitea.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-24 11:14:33 -04:00
awkawbandClaude Opus 5 a7d8300ce9 Narrow the ORM/caching non-goal to what is actually ruled out
CI / test (push) Canceled after 0s
CI / Build on the declared MSRV (push) Canceled after 0s
CI / Latest stable (advisory) (push) Canceled after 0s
"No ORM, session, or caching layer" bundled several decisions with very
different costs, and read as refusing things that are in fact fine.

Split into the three that genuinely conflict, each with its reason:
lazy loading (models would have to hold a client handle, breaking
`Default`-constructibility and the `extra` round trip), dirty tracking
(cannot diff a flattened map of unmodelled fields), and transparent
entity caching.

The caching reason is the one worth recording: `BaseController`'s shared
response path sends no `ETag`, `If-None-Match`, or `Cache-Control`, so a
cached entity cannot be cheaply revalidated. On an API that handles money
that is a correctness hazard rather than a missed optimisation. Writing
the evidence down so the question is not reopened without it.

Two things the old wording excluded by accident are now open questions
instead: typed `include` helpers, since eager loading is a stringly
parameter where a typo is a silent no-op; and an opt-in cache for
`/statics`, which is near-constant and not money. Also parks the
`updated_at` delta-sync parameter as the shape a local mirror would
need, noted as a different product rather than a plan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-23 19:43:19 -04:00
awkawbandClaude Opus 5 fbde9a9bb7 Write down the project's goals, non-goals, and roadmap
Several decisions in here look like oversights until you know the
reasoning: the curated model subset with everything else in `extra`, the
still-generic `chart`, not retrying `POST`, the deliberate divergences
from the official PHP SDK. That reasoning was spread across code
comments and commit messages, where it is easy to miss and easy to
"fix".

GOALS.md states:

- What the crate is for: complete coverage, and not quietly losing data,
  truncating results, or succeeding when it did not.
- Seven goals, each tied to a decision visible in the code, so the
  rationale survives the next refactor.
- Non-goals, including no sync API, no caching or ORM layer, no
  modelling of every field, and no retrying of non-idempotent requests.
- Publication posture: on crates.io, pre-1.0, no stability promise, pin
  an exact version. MSRV may rise with dependencies.
- A roadmap ordering the COVERAGE.md gaps by value over effort, starting
  with multipart uploads, which is the only thing blocking
  `create_document`.
- Open questions, left open rather than papered over: whether auth routes
  belong here at all given they are a different auth model, whether
  per-chart types earn their keep, streaming for large exports, and the
  untested `Error::PaginationLimit`.

README, COVERAGE.md, and CLAUDE.md link to it. No changelog entry: this
adds no behaviour, per the rule in CLAUDE.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-23 19:25:59 -04:00