discovery-media-player 0.1.156 → 0.1.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/HOST-CONTRACT.md +141 -0
- package/package.json +1 -1
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -947,6 +947,20 @@ previous version claimed nothing. Four hours after the release, a host measured
|
|
|
947
947
|
deployment caps, times out, paginates, or rewrites anything between us and your database, that
|
|
948
948
|
limit is invisible from here and our arithmetic is probably wrong about it.
|
|
949
949
|
|
|
950
|
+
⚠️ **If you sweep your own reads for this, sort them by what the value BECOMES, not by how many rows
|
|
951
|
+
it holds.** This criterion is not ours: a host swept theirs, closed all of them, and reported that
|
|
952
|
+
the row count had been the wrong axis the whole time. The expensive ones were the **aggregations** —
|
|
953
|
+
a credit balance summed with a `reduce`, a read counter taken as `rows.length`, a visit tally
|
|
954
|
+
incremented per row. A truncated sum does not look truncated: **it is wrong *and* plausible**, which
|
|
955
|
+
is worse than the ceiling's usual symptom, a list that at least renders visibly stale. Their credit
|
|
956
|
+
ledger stood at 503 rows and grows on every AI call — the invoice would have gone wrong before the
|
|
957
|
+
table ever looked big enough to suspect.
|
|
958
|
+
|
|
959
|
+
So the useful question about one of your reads is not *"could this exceed 1000?"* but **"is this
|
|
960
|
+
value aggregated, or displayed?"** A displayed list degrades visibly. An aggregate degrades into a
|
|
961
|
+
number someone will believe. Truncation you have decided to keep is fine and cheap to make honest —
|
|
962
|
+
theirs kept one, bounded to 1000 and stated rather than assumed.
|
|
963
|
+
|
|
950
964
|
**2. Something this card asserts that you can check against your own database.** Not "does it look
|
|
951
965
|
right" — *does this number match what a query returns right now*. The purge card is the obvious one:
|
|
952
966
|
`vide: true` is what authorises dropping a column, so a wrong `true` is expensive and a wrong `0`
|
|
@@ -991,6 +1005,80 @@ measurement came from rather than only what it said.
|
|
|
991
1005
|
The two kinds want opposite things from us: a debt should be chased, a structural limit should be
|
|
992
1006
|
written down and stopped being asked about.
|
|
993
1007
|
|
|
1008
|
+
⚠️ **And there is a third kind, which we proposed as a false choice and a host corrected.** We asked
|
|
1009
|
+
another host to sort two backup figures — the PITR window, and the age of the oldest snapshot — into
|
|
1010
|
+
debt or structural limit. Their answer was neither, and the distinction is operational rather than
|
|
1011
|
+
pedantic:
|
|
1012
|
+
|
|
1013
|
+
- **Not measurable by a session.** Checked against two independent toolsets — a second host's and
|
|
1014
|
+
their own — neither the platform API nor the MCP tools expose either figure. The one tool with a
|
|
1015
|
+
near-enough name restores a *paused* project.
|
|
1016
|
+
- **Measurable by a human.** An authenticated dashboard session displays both.
|
|
1017
|
+
|
|
1018
|
+
So it is a debt whose payer is *necessarily a person*, and that is what makes it its own kind: it
|
|
1019
|
+
behaves like a structural limit toward every automated agent (chasing it changes nothing, no session
|
|
1020
|
+
will ever produce it), and like a debt toward the installation (someone can pay it, and until they
|
|
1021
|
+
do, the purge is not datable end to end). Their own phrasing, which we adopt: *"no session can
|
|
1022
|
+
render them; a human can, and until one has, the purge is not datable end to end."* They had raised
|
|
1023
|
+
it with theirs four times.
|
|
1024
|
+
|
|
1025
|
+
The practical consequence for us is a rule about **who we are asking**, which we had never written:
|
|
1026
|
+
a question a host cannot answer with the tools they run on is not answered by asking again. Either
|
|
1027
|
+
it reaches a person, or it should be recorded and dropped.
|
|
1028
|
+
|
|
1029
|
+
⚠️ **Where the substitution seam is, because a host asked and the answer was not written anywhere.**
|
|
1030
|
+
Their question came from their own defect: they had written a pagination helper after an incident,
|
|
1031
|
+
with its reason at the top, and it was called *nowhere*. The cause turned up only when they tried to
|
|
1032
|
+
use it — it called the **local binding** of their database client, while their benches replace the
|
|
1033
|
+
**export**, so adopting it broke the very bench that covered it. Their sentence is the part worth
|
|
1034
|
+
keeping: *"nobody writes «I don't use it because it breaks my doubles» — you give up in silence."*
|
|
1035
|
+
A non-substitutable helper produces no red, no complaint, no trace. It produces an absence of use,
|
|
1036
|
+
which nothing distinguishes from a need that never existed.
|
|
1037
|
+
|
|
1038
|
+
Asked of us, the answer has two halves and only one of them is a promise:
|
|
1039
|
+
|
|
1040
|
+
- **The injected context is substitutable, and that is the seam.** Every call reads `PLAYER.<member>`
|
|
1041
|
+
at the moment it fires; nothing captures it into a local binding at module load, before you have
|
|
1042
|
+
injected anything. Measured on 2026-09-05 across `server/` and `context/`: **144 calls, 0
|
|
1043
|
+
captures.** Your double of `db.request` is the one that runs. `tools/couture-substituable.mjs`
|
|
1044
|
+
now refuses a capture, so this stays true rather than happening to be true today.
|
|
1045
|
+
- **Our exports are not substitutable among themselves, and we do not promise they are.** Same date,
|
|
1046
|
+
same zones: **64 of 75 exported names are also called internally through their local binding.**
|
|
1047
|
+
If you stub `getShareBySlug` on the module we export and expect `overview()` to see your stub, it
|
|
1048
|
+
will not. That is ordinary CommonJS and we are not changing 64 call sites for it — but you would
|
|
1049
|
+
have discovered it the expensive way, so it is written here instead.
|
|
1050
|
+
|
|
1051
|
+
Bring us the seam you need and cannot get, rather than working around a missing one in silence.
|
|
1052
|
+
|
|
1053
|
+
⚠️ **What every report must carry: the version you measured on.** One field, and it is the one we
|
|
1054
|
+
kept losing. `version` is already in the identity card — `GET /api/doc` serves it — so this costs
|
|
1055
|
+
you a copy-paste and nothing else: *"measured on 0.1.146"*, beside the shape and the count.
|
|
1056
|
+
|
|
1057
|
+
This is here because a host showed that we had asked for the wrong thing. We had asked hosts to stay
|
|
1058
|
+
current, reasoning that we cannot act on a report we cannot reproduce. Their correction: **what we
|
|
1059
|
+
need is not that you are up to date, it is knowing which version the measurement came from** — and a
|
|
1060
|
+
stamped report from a host eleven releases behind is reproducible, while an unstamped one from a
|
|
1061
|
+
perfectly current host is not. We then measured this document: of twelve host findings recorded in
|
|
1062
|
+
it, **none** names the version it was measured on. Some of those hosts were current at the time. The
|
|
1063
|
+
information was lost when we wrote it down, and no amount of future alignment brings it back.
|
|
1064
|
+
|
|
1065
|
+
So: **shape, count, version.** If the measurement spans an upgrade, say both. If you no longer know
|
|
1066
|
+
which version a past observation came from, say that too — *"measured some time before 0.1.150"* is
|
|
1067
|
+
worth more than a number we would have to guess at, and far more than silence.
|
|
1068
|
+
|
|
1069
|
+
⚠️ **And name yourself inside the report, not just in how you send it.** Reports reach us through
|
|
1070
|
+
whatever channel carries them, and a channel can deliver the same message twice or put one host's
|
|
1071
|
+
text under another host's name — both happened to us in a single round, and we spent an exchange
|
|
1072
|
+
establishing who had said what instead of acting on it. Neither you nor we could tell from our own
|
|
1073
|
+
end; only the person relaying could, and they did. **One line of self-identification in the body
|
|
1074
|
+
costs nothing and survives any relay** — the same reasoning as the version stamp, applied to *who*
|
|
1075
|
+
rather than *what*.
|
|
1076
|
+
|
|
1077
|
+
The consequence for us is worth stating too, since it is about how much we can claim to have heard:
|
|
1078
|
+
when a duplicate is resolved, the reading it seemed to provide does not turn up elsewhere. It leaves
|
|
1079
|
+
us with one fewer host heard from, and we would rather record that plainly than let a channel look
|
|
1080
|
+
wider than it is.
|
|
1081
|
+
|
|
994
1082
|
**What not to send.** Shapes and counts, never contents. No row data, no reader IPs or User-Agents —
|
|
995
1083
|
those are the columns half this contract exists to get rid of — no keys, tokens, connection strings,
|
|
996
1084
|
or private hostnames. *"A table of ~1600 rows returned 1000"* is the whole of what we needed to fix
|
|
@@ -1001,8 +1089,61 @@ dated entry naming the case. That last part is deliberate and a host asked for i
|
|
|
1001
1089
|
justification rots, a dated incident does not. In six months someone will read a field and ask why
|
|
1002
1090
|
it exists, and the answer will name you.
|
|
1003
1091
|
|
|
1092
|
+
⚠️ **But that date is ours, and for a long time we mistook it for yours.** Every changelog section
|
|
1093
|
+
carries the day *we shipped the fix* — 155 out of 155, checked. None carries the version *you*
|
|
1094
|
+
measured on, and the two answer different questions: ours says when it was closed, yours says what
|
|
1095
|
+
the observation was of. We had that backwards long enough to ask hosts for the wrong thing, so it is
|
|
1096
|
+
worth stating plainly rather than quietly correcting: **the entries above this line are unstamped,
|
|
1097
|
+
and cannot be retro-stamped** — the versions they were measured on were never recorded and are not
|
|
1098
|
+
recoverable. Everything from here on carries the stamp you send.
|
|
1099
|
+
|
|
1004
1100
|
## Versioning
|
|
1005
1101
|
|
|
1006
1102
|
Semantic versioning on the package, independent of the `contract` number. Pin an **exact** version:
|
|
1007
1103
|
the player and its hosts deploy separately, and a range brings in a version nobody decided to
|
|
1008
1104
|
deploy, on a day someone ran `npm install` for another reason.
|
|
1105
|
+
|
|
1106
|
+
⚠️ **When we say a release "changes nothing for you", check it from *your* version — ours is not
|
|
1107
|
+
yours.** The zone table in every Release compares the new version to **the one immediately before
|
|
1108
|
+
it**. That is our convenience, not your situation: a host two or three releases back is looking at a
|
|
1109
|
+
different diff, and theirs is the one that decides. A host caught this by redoing it — we had
|
|
1110
|
+
compared `0.1.156 → 0.1.157`; they were jumping from `0.1.155`, ran their own comparison, and got a
|
|
1111
|
+
third file we had not mentioned. Same conclusion in the end (no migrations, zero lines of code in
|
|
1112
|
+
`server/` once comments are excluded), **but reached on their span rather than on our word.**
|
|
1113
|
+
|
|
1114
|
+
So take the reassurance as a starting point and not as a finding. The tarballs are public: `npm pack`
|
|
1115
|
+
both versions and `diff -rq` the two trees is a minute's work, and it is the only version of the
|
|
1116
|
+
question that is about your installation.
|
|
1117
|
+
|
|
1118
|
+
⚠️ **We asked you to stay current. That was the wrong ask, and a host took it apart.** The previous
|
|
1119
|
+
version of this paragraph requested that hosts keep the pin no more than one release behind, on the
|
|
1120
|
+
grounds that *a report we cannot reproduce is a report we cannot act on.* The premise is right. The
|
|
1121
|
+
conclusion did not follow:
|
|
1122
|
+
|
|
1123
|
+
> You are asking that hosts be up to date; what you need is to know **which version a measurement
|
|
1124
|
+
> was taken on**. They are not the same thing, and the second is strictly cheaper.
|
|
1125
|
+
|
|
1126
|
+
They are exactly right, and the counter-example is decisive. A host eleven releases behind who
|
|
1127
|
+
writes *"measured on 0.1.146: the 1600-row table returned 1000"* has given us a reproducible report.
|
|
1128
|
+
A perfectly aligned host who writes *"it returns 1000"* has not — and we find out only on the day we
|
|
1129
|
+
try to replay it. **Alignment neither implies the stamp nor substitutes for it.**
|
|
1130
|
+
|
|
1131
|
+
⚠️ **And it is measurable in this very document — which is how we know the drift was never the
|
|
1132
|
+
cause.** They counted twelve places where this contract reports a host's finding and found one
|
|
1133
|
+
carrying a version nearby. We re-measured rather than take it: of those twelve, **zero** carry the
|
|
1134
|
+
version the host measured on. The single dated line in this file stamps *our own* measurement of our
|
|
1135
|
+
own code, not anyone's report. Several of those findings came from hosts who were current at the
|
|
1136
|
+
time. **The information was lost in the writing, not in the deployment, and no future alignment
|
|
1137
|
+
restores it.**
|
|
1138
|
+
|
|
1139
|
+
⚠️ **Worse: the ask had the shape of the bias we had just written against ourselves.** A request to
|
|
1140
|
+
stay aligned can only be honoured by hosts who answer us, and is invisible in hosts who have
|
|
1141
|
+
drifted — so the channel would have reported "hosts are aligned" because the aligned are the only
|
|
1142
|
+
ones we hear from. That is the instrumentation bias one level up, applied to cadence instead of
|
|
1143
|
+
yield. The host who named it also took the release anyway, which is why the correction is theirs and
|
|
1144
|
+
not a concession.
|
|
1145
|
+
|
|
1146
|
+
**So the ask is now the stamp, and it is above, in what a report must carry.** Staying current keeps
|
|
1147
|
+
a smaller and separate value — you get fixes sooner, and a release that changes nothing for you
|
|
1148
|
+
costs nothing to take — but it is no longer presented as what makes your reports usable. It never
|
|
1149
|
+
was.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.158",
|
|
4
4
|
"description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pdf-viewer",
|