Skip to content

feat: the companion can now actually send a night - #1

Merged
FriskyyDev merged 1 commit into
mainfrom
feature/upload-ui
Aug 4, 2026
Merged

feat: the companion can now actually send a night#1
FriskyyDev merged 1 commit into
mainfrom
feature/upload-ui

Conversation

@FriskyyDev

Copy link
Copy Markdown
Owner

The app could read the saved-variables file and had a client method for uploading. Nothing connected the two, and the window was a placeholder that said so. This is the part where it does the job.

Depends on FriskyyDev/raidify#69 for the guild list endpoint.

Setup: three questions, asked once

Who are you, which guild does this machine report for, where is the game. Every extra question here is a reason to close the app and go back to pasting a string, which already works. Once answered it collapses to a single line — setup is not what an officer opens this app to look at.

It never sends on its own

The officer knows whether Tuesday's session on an alt was the guild's Naxx run or three people messing about in Deadmines. Nothing in the file distinguishes those. So it reports what it read and waits.

Review runs a dry run against the real server path, so what's shown is what will happen rather than a second guess at it. Send stays disabled until a review has happened.

Two things a night can be that need saying out loud:

  • Older than two weeks — sending rewrites the record for that date. Sometimes the point; sometimes a five-month-old raid being overwritten.
  • Not ended — the addon never closed the session, so anyone who joined later is missing. The fix is /rf end in game, not anything in this app.

Rows nobody accounted for are labelled "nobody said". Not "absent". That's the accusation the addon refuses to make, and the UI doesn't get to make it on the addon's behalf.

Settings

Plain JSON — none of it is a secret, and the token still lives behind the OS credential store.

The stored file path is not trusted on the next launch. It's re-discovered by scanning the install folder it came from, so the rule stays "nothing is read that discovery did not produce", with no exception for a string that happens to be written down.

Writes go through a temp file and a rename. The alternative is a Windows update killing the process mid-truncate, turning "forgot a preference" into a zero-byte settings file.

The upload history isn't a correctness mechanism — the server upserts, so a second send changes nothing. It's so the list stops asking. Every flush re-reads every session in the file, and an app that re-offers the same finished night at every launch teaches the officer to ignore the list.

Wired up a callback that was doing nothing

The watcher's onEmpty existed and nothing consumed it, so a clean read finding no session was silent. The UI couldn't tell "read it, nothing there" from "never read it" — one of which needs the officer to do something. It says which now.

Two guards, both mutation-checked

The smoke test now asserts the window rendered text, not just that the bridge answers. A perfect bridge and a blank window is a real combination: an error thrown in App() leaves an empty root while every existing check passes. Verified by rendering nothing — SMOKE FAIL: the window rendered nothing (body: "").

And it refuses to run against a stale build. npm run build typechecks first, so a type error leaves out/ untouched and the smoke test then starts yesterday's build and reports success — a green result for code that does not compile. Found by accident while mutation-checking the render assertion, which is exactly how it would have been found later and at worse cost.

Verification

  • 68 tests passing (9 new, covering settings persistence, corrupt-file recovery, wrong-type rejection, history bounds, and night-key stability across the Date/string IPC boundary)
  • Typecheck clean on both tsconfigs
  • SMOKE OK: bridge exposes 18 calls, appInfo returned v0.1.0, and the window rendered.

Also

Stopped tracking *.tsbuildinfo — machine-local, churns on every build.

🤖 Generated with Claude Code

It could read the saved-variables file and had a client method for
uploading. Nothing connected the two, and the window was a placeholder that
said so. This is the part where it does the job.

Setup is three questions asked once: who are you, which guild does this
machine report for, where is the game. Every extra question here is a reason
to close the app and go back to pasting a string, which already works. Once
answered it collapses to a single line, because setup is not what an officer
opens this thing to look at.

It never sends on its own. The officer is the one who knows whether
Tuesday's session on an alt was the guild's Naxx run or three people messing
about in Deadmines, and nothing in the file distinguishes those. So it
reports what it read and waits. "Review" runs a dry run against the real
server path, so what is shown is what will happen rather than a second guess
at it — and "Send" is disabled until a review has happened.

Two things a night can be that need saying out loud. Older than two weeks:
sending it rewrites the record for that date, which is sometimes the point
and sometimes a five-month-old raid being overwritten. Not ended: the addon
never closed the session, so anyone who joined later is missing, and the fix
is /rf end in game rather than anything here.

Rows nobody accounted for are labelled "nobody said". Not "absent". That is
the accusation the addon refuses to make, and the UI does not get to make it
on the addon's behalf.

Settings persist as plain JSON — none of it is a secret, and the token still
lives behind the OS credential store. The stored file path is not trusted on
the next launch: it is re-discovered by scanning the install folder it came
from, so the rule stays "nothing is read that discovery did not produce",
with no exception for a string that happens to be written down. Writes go
through a temp file and a rename, because the alternative is a Windows
update killing the process mid-truncate and turning "forgot a preference"
into a zero-byte settings file.

The upload history is not a correctness mechanism — the server upserts, so a
second send changes nothing. It is so the list stops asking. Every flush
re-reads every session in the file, and an app that re-offers the same
finished night at every launch teaches the officer to ignore the list, which
is the one thing it must not do.

The watcher's onEmpty callback existed and nothing was wired to it, so a
clean read that found no session was silent. The UI could not tell "read it,
nothing there" from "never read it" — one of which needs the officer to do
something. It says which now.

Two guards, both mutation-checked:

The smoke test now asserts the window rendered text, not just that the
bridge answers. A perfect bridge and a blank window is a real combination:
an error thrown in App() leaves an empty root while every existing check
passes. Rendering nothing makes it fail.

And it refuses to run against a stale build. npm run build typechecks first,
so a type error leaves out/ untouched and the smoke test then starts
yesterday's build and reports success — a green result for code that does
not compile. That is the same shape as the bug this test exists to catch.
Found by accident while mutation-checking the render assertion, which is
exactly how it would have been found later and at worse cost.

Also: stop tracking tsbuildinfo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FriskyyDev
FriskyyDev merged commit 4afd0c6 into main Aug 4, 2026
1 check passed
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