Skip to content

0.3.0: freeform client fields and a payee - #2

Merged
ralyodio merged 1 commit into
mainfrom
freeform-clients
Aug 29, 2026
Merged

0.3.0: freeform client fields and a payee#2
ralyodio merged 1 commit into
mainfrom
freeform-clients

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The half that has to exist before moshcode's /client can move out.

moshcode keeps contact details the way they arrive — a comma form for what you pasted out of a signature, --a.b dotted flags for everything else, and no fixed field list. This model had columns and nothing else, so moving a client out of ~/.moshcode/business.json would have dropped every field nobody predicted. A lossy migration is one nobody should run, which is why the delegation in moshcode is still behind MOSHCODE_EXTERNAL_BILLING.

billing client add "Acme Inc", https://acme.com, +1-555-0100
billing client add acme --contact.telephone +1-555-0100 --contact.name Jane
billing client payee acme solana:9xQe...

The rules that earned their lines

  • The comma form reads by shape, not position — nobody's signature comes in a fixed order. A segment nothing recognises is kept as a note rather than dropped: losing a line somebody pasted is worse than filing it imprecisely.
  • Any dotted flag sets that path, and merges onto what is there, so setting one field cannot drop the rest.
  • An undotted unknown flag is still an error. Freeform fields must not turn every typo into a silently accepted field.
  • setPath refuses __proto__, constructor, prototype. These paths come straight off a command line, so --__proto__.x has to be a field called __proto__ and not a write to Object.prototype. There is a test asserting the prototype is clean afterwards.
  • A payee is recorded, never guessed. A bare address with no chain: prefix and no --chain is filed as unknown rather than assumed, and client payee acme with no address is refused rather than clearing the one already there — a typo must not silently change where money goes.

invoice render --format json now carries the payee, which is how an outside payment rail asks where to settle. moshcode's /payments stays where it is and reads that.

billing import carries the lot: every key moshcode kept that has no column here travels as-is, and the columns that do exist are not duplicated into fields. There is a test for exactly that round-trip.

129 tests. This is also the first release to go out through the new publish.yml rather than by hand.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KVUZsx4WBZK5rnJJFGgwF5

…e out

moshcode's /client keeps contact details the way they arrive — a comma form for
what you pasted out of a signature, --a.b dotted flags for everything else, and
no fixed field list. This model had columns and nothing else, so moving a client
out of ~/.moshcode/business.json would have dropped every field nobody
predicted. A lossy migration is one nobody should run, which is why the
delegation in moshcode is still behind a flag.

This is the half that had to exist first.

  billing client add "Acme Inc", https://acme.com, +1-555-0100
  billing client add acme --contact.telephone +1-555-0100 --contact.name Jane
  billing client payee acme solana:9xQe...

The comma form reads by shape rather than position, because nobody's signature
comes in a fixed order, and a segment nothing recognises is kept as a note
rather than dropped — losing a line somebody pasted is worse than filing it
imprecisely. Any dotted flag sets that path and merges onto what is there, so
setting one field cannot drop the rest. An *undotted* unknown flag is still an
error: freeform fields must not turn every typo into a silently accepted one.

setPath refuses __proto__, constructor and prototype. These paths come straight
off a command line, so --__proto__.x has to be a field called __proto__ and not
a write to Object.prototype. There is a test that asserts the prototype is
clean afterwards.

payee is recorded, never guessed. A bare address with no chain: prefix and no
--chain is filed as "unknown" rather than assumed, and `client payee acme` with
no address is refused rather than clearing the one already there — a typo must
not silently change where money goes. `invoice render --format json` carries it,
which is how an outside payment rail asks where to settle; moshcode's /payments
stays where it is and reads that.

`billing import` carries the lot: every key moshcode kept that has no column
here travels as-is, and the columns that do exist are not duplicated into
fields.

129 tests.
@ralyodio
ralyodio merged commit 7e20aeb into main Aug 29, 2026
10 checks passed
@ralyodio
ralyodio deleted the freeform-clients branch August 29, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant