What was built, and how to check it
Nothing here is a claim. Every number was written by a script that ran the test suites for real, the three files it reads are served from this site, and anything that cannot be shown is said to be missing rather than filled in.
The suites, as they last ran
apps/web/public/proof/tests.json · generated 2026-09-19T21:06:24.799Z
Every clause, and the tests that hold it
apps/web/public/proof/spec-map.json · test names from tests.json
The technical specification this product was written against, section by section. Open one to read its clauses and the test files that pin them. A citation is a pointer, not a measurement: one file usually pins several clauses, so nothing here adds up to a score, and which of a file’s tests are about which clause is a human judgement left there to be disagreed with.
67 clauses pinned · 10 partly · 3 nothing pins them
1. Overview and Thesis 3 pinned · 1 partly
A memory SDK and MCP server that merges subagents' memory into one project memory
A lead agent that never attended the earlier sessions receives the shared context of the agents that did.
The core is merging: sources preserved, duplicates consolidated in retrieval, related records grouped, contradictions kept visible
Merging returns one item for an exact duplicate and groups related records, without destroying any source record or hiding a conflict.
OFFCUT is not a relayer: it stores, merges and retrieves memory itself
The memory path stores, merges and retrieves here: the whole cycle completes with every outbound call trapped, and the path carries no HTTP client at all.
Pinned as the negative it is. Forwarding is the thing that must not happen, so the whole cycle — add, import, merge, recall, inspect, export, forget — is run with fetch throwing, the record is read straight out of the store tables, the duplicate collapse is recomputed after the client is thrown away, and the memory path is swept for any HTTP client. It does not prove that nothing is reached by some means other than fetch; the sweep of the files themselves is what stands in for that.
Product boundary: no watermelon trading
No trades, swaps, arbitrage, market monitoring, portfolios or transaction signing anywhere in the product.
Two of the three places this could go wrong are asserted now: the site’s language, and the product’s own API, whose complete tool list is pinned exactly and swept for trading vocabulary, for wallet and chain parameters, and for anything a market would need. What no test does is sweep every file in the workspace for a trading surface somebody could add without adding a tool.
2. Entities and Roles 6 pinned
A workspace is one project's memory, scoped to selected agents
A workspace is not a common database: records never cross from one workspace to another.
A source record carries an author and a source, kept separate from its evidence
Authorship and supporting evidence are separate fields on every record, and an unknown record type is refused rather than stored.
Merged memory references the source records and versions it was built from
A derived block pins the exact versions it used and goes stale when one of them changes.
Context is the relevant portion of merged memory within permissions and a size limit
A recall answers within the caller’s permissions and the size limit it was given, says when it had to stop, and takes nothing out of the store.
Both halves are pinned now: an answer spends no more than the budget it was handed and marks itself incomplete, it never spends that budget on records the caller may not see, and shortening removes nothing from the store. What the numbers should be was OPEN-3; they exist in LIMITS today and the refusal past the maximum is pinned too, but no test claims those values are the right ones.
$OFFCUT is an asset separate from memory
The token is not a memory mechanism; the memory core runs with no wallet, chain or token configured.
The spec's "product utility is [OPEN]" was answered by the owner on 2026-09-15; see the owner-decisions section.
No participant acquires powers it was not granted
An agent cannot grant itself permissions, create another agent, or act as the owner; identity comes from the connection.
3. Mechanics 16 pinned
Connection: MCP server or SDK, a workspace, granted access, results written through memory tools
An agent authenticates, writes through the memory tools, and the next session reads the same workspace back.
Existing notes enter only through an explicit JSON import; connection is not automatic access to every chat
Nothing enters memory that was not written or imported on purpose, and no tool offers a way to ingest anything by itself.
Pinned from both ends. In the core, a workspace whose agents have all connected and authenticated is still empty until a record is handed over deliberately, and an agent that was not granted import is refused. On the MCP surface, no tool takes a path, a folder, a url or a chat export, so the material an import loads has to be carried in the call itself.
A record's fields, and agentId taken from the verified connection
An agentId supplied in a request body is ignored; the writer is the connection that carried the write.
Every write carries an idempotency key; a replay returns the previous result and a reused key with new content is rejected
Retrying a write creates no second record, and the same key carrying different content is refused.
A write is acknowledged after it is persisted; a correction is a new version and a stale version cannot overwrite
Acknowledged records survive a restart, and a correction built on a stale version is refused on both surfaces.
Merging happens inside one workspace and one authorized audience
A private record does not become shared because its topic matches, and records never merge across workspaces.
The merge table: a duplicate returns one item, related items group, a conflict is flagged, an authorized correction updates the current version
Each row of the merge behaviour table, with every source record left intact.
An uncertain semantic relationship leaves the records separate
An ambiguous pair stays two items: nothing is discarded, and nothing is joined, for a cleaner summary.
Pairs a similarity model would happily fold together are fed to the engine and stay separate — two items, no collapse, and no conflict invented out of free text — with a control proving the engine does act once the relationship is stated as an explicit signal. Without that control the other assertions could be satisfied by an engine that merges nothing at all.
A derived block cites source IDs and versions; a stale block is rebuilt before it is served as current
Shared memory is records and relationships, not one enormous prompt, and staleness clears only by rebuilding.
Retrieval checks access first; inaccessible and deleted records contribute to nothing
A foreign or revoked caller gets no content, no summary and no metadata, and a deleted record leaves no trace in a count.
A recall returns text, sources, versions and unresolved conflicts; a budget too small returns a flag and references, never one side
Both sides of a conflict stay visible however small the budget, and two equally-permissioned callers get identical context.
No data means an empty result, never an invented answer
Memory is not replaced with a guess: no data means an empty result, and a question that matches nothing returns stored text and says so.
Three shapes of “no data” are pinned directly — an empty workspace, a workspace where every record is another agent’s private one, and a workspace whose only record has been deleted — plus the line the builder does not cross when records exist but the words miss: it says so in a note and returns stored text rather than writing an answer of its own.
Access checks apply to cached results; revocation blocks what follows
A revoked key is refused on the very next request, and every later operation by that principal fails.
A conflict resolution records the chosen version, the rationale and the decision author
A conflict reappears on every recall until an authorized resolution names who decided, and reopens when a third value arrives.
Deletion removes the record and its dependent blocks from every subsequent response; indexes and managed copies are cleaned
A deleted record returns through no path, and restoring a snapshot does not resurrect one.
The end-to-end example: three agents, one question, one merged context
A new lead agent answers "what remains before release?" from three sessions it never attended, and a later fix does not erase the bug report.
The specification's own example is the suite's PRIMARY acceptance check, which replays it: the same question, the same three sessions.
4. Components and Roles 6 pinned · 1 partly
SDK and MCP server run on one core, with one access layer, store, merge engine and context builder
The same query through the SDK and through MCP returns the same context, and a write through one is readable through the other.
The first release is local; every connection to a workspace uses the same store
One store per installation, created and migrated on first use, with its location decided by one documented variable.
The store is Prisma over SQLite or PostgreSQL. The spec's final choice is OPEN-2 and is still the owner's to ratify.
The eight operations — add, import, merge, recall, inspect, resolve, forget, export — on both surfaces
Every operation exists under the name the spec gives it, on the SDK, over MCP and over HTTP.
Trust boundary: permissions belong to a verified connection
A client that cannot distinguish subagents is treated as one principal, and the key is the identity.
Trust boundary: memory text is data, never instructions
A record saying "ignore the rules" is stored verbatim and buys nothing, and the model is told the same about record text.
Trust boundary: local storage is protected by the operating system, not by SDK checks
A process with direct access to the database file is outside what OFFCUT can defend, and the code does not pretend otherwise.
What a test can pin is the shape of the boundary, and it does: the permission check holds on the path the SDK owns, and raw SQL against the same file walks straight past it and leaves no audit trail — openly, by design, so a quietly added tamper seal would turn this red. The other half of the clause, that operating-system permissions are what protect the file, is about the operating system and stays outside the suite.
Trust boundary: text leaves the system only with explicit owner permission, disabled by default
Nothing is sent to an external model until an owner turns it on, and only an owner can turn it on.
5. Economics 4 pinned · 1 partly · 1 unpinned
Local SDK and MCP need no wallet, gas or token purchase
The full write, merge, recall, delete and export cycle runs with no wallet, chain or model configured.
Storage and compute come from the user's infrastructure; pricing and licensing are undecided (OPEN-7)
No paid tier exists, and none is described anywhere.
Unpinned because it is undecided, not because it is unfinished work: pricing and licensing are a commercial decision (OPEN-7), and nothing in the product claims a price, so there is nothing for a test to hold to.
no test pins this
The token is named OFFCUT AGENT with the symbol OFFCUT
The name, the symbol and the ticker stay in step wherever they are printed.
Token launch: Robinhood Chain 4663, pons v2; launch parameters are OPEN-7
Until an address is supplied the site says there is none; once one is, it is validated, shown in full, copied in full and linked.
No contract address has been supplied, so the launched state is pinned by tests rather than demonstrated by a deployment.
Token utility is undecided; no invented staking, tax, buyback or payouts
The section says the role is open rather than filling itself with a mechanic nobody agreed to.
Superseded in part on 2026-09-15: the owner deliberately added a reward program, so OPEN-7 is no longer fully open. The check was replaced rather than deleted — the page may describe that program and no other mechanic. See the owner-decisions section.
Storing memory is not an onchain operation, and holding the token grants no access to other users' records
Memory and the token touch nowhere: the core does not know whether a token exists, and a payout address is not a sign-in.
6. Invariants 10 pinned
Invariant 1 — every record has a workspace, author, source and version; merging destroys no source
Authorship and evidence are separate fields, a superseded version stays readable, and merging leaves every source intact.
Invariant 2 — merging never broadens access
A private record stays out of a shared block even when the topic matches, and nothing merges across workspaces.
Invariant 3 — a retried write creates no second record; identical text from another author keeps its provenance
The stored result replays for the same key, a reused key with new content is rejected, and two agents saving the same sentence keep both names.
Invariant 4 — acknowledged records survive a restart; concurrent corrections never silently overwrite
Everything acknowledged reads back after reconnecting, a stale correction is refused, and exactly one of two racing corrections wins.
Invariant 5 — a detected conflict cannot vanish without a recorded resolution or a deletion
The conflict is reported on every recall, closes only through a resolution that names its author, and reopens on a third value.
Invariant 6 — derived memory cites real source versions, and a stale summary is never served as current
A block is pinned to the versions it was built from, marked stale when one moves, and cleared only by a rebuild.
Invariant 7 — a deleted record never returns through search, export, cache or a dependent summary
All four paths are checked, the payload is wiped, the tombstone stays honest, and a restore replays the deletion.
Invariant 8 — SDK and MCP enforce the same access and mutation rules
Two equally-permissioned callers get byte-identical context, and permission flags apply the same whichever operation is called.
Invariant 9 — memory text grants no permission, and revocation blocks what follows
An injection attempt is stored verbatim and buys nothing; once access is revoked every later operation fails.
Invariant 10 — the local core runs without the token or an external model
The whole cycle completes with nothing configured, and an import's claimed author never becomes the verified one.
7. Risks and Calibration 8 pinned · 1 partly · 1 unpinned
Risk: combining different facts because the wording is similar
Only exact duplicates are merged; an ambiguous pair is meant to stay separate.
The mitigation is pinned from both sides now — merging leaves every source intact, and pairs that only look alike are left alone while an explicit signal still merges them. What is missing is the calibration the section asks for: there is no labelled set of duplicates, complements and conflicts to measure against, and the semantic thresholds are OPEN-4.
Risk: losing context through shortening
Source records are preserved, and a truncated answer carries references and an incompleteness indicator.
Both halves of the mitigation are asserted: a shortened answer carries references and says it is incomplete, and every source record is still in the store afterwards, unchanged. What no test does is judge how much shortening is acceptable — that is a calibration nobody has measured, and the numbers in LIMITS are chosen rather than derived from one.
Risk: cross-project leakage — negative tests on every path
Retrieval, merge, cache, export and the HTTP surface each refuse an outsider, and answer identically for a workspace that does not exist.
Risk: author spoofing through agentId
Identity comes from the connection, and an imported claim of authorship is stored separately from it.
OPEN-1, the client connection design, is still open; what is pinned is this server's own behaviour.
Risk: write conflicts or process failure
Transactions, versions and idempotency keys, exercised with concurrent writes and forced restarts.
Risk: malicious instructions inside memory
Record text never authorizes anything, and the model is told the text is data.
The spec says so itself: what an external agent does with the text it was handed is not something OFFCUT controls.
Risk: deleted information remains in a copy
Derived data is invalidated, indexes cleaned, snapshots pruned, and the deletion ledger is never pruned.
Retention defaults to 30 days in code. Whether 30 days is the policy is OPEN-5, which is the owner's to settle.
Risk: an external model or its summary is wrong
The module is off by default, and a failure costs the summary and nothing else.
The authorized provider and the quality checks are OPEN-4.
7.1 Acceptance — the eight checks and the primary check
Every row of the acceptance table is a test, and the primary check demonstrates a new agent continuing a task from merged memory.
Before release: supported clients, data format, limits and deletion policy must be fixed
A release gate, not a behaviour.
Unpinned because it is a release gate rather than a behaviour: the items in it are OPEN-1, OPEN-2, OPEN-3 and OPEN-5, they belong to the owner and the team, and no test can take a decision.
no test pins this
8. Open Questions 0 pinned · 6 partly · 1 unpinned
Which MCP clients are supported, and how do they separate permissions between subagents?
Undecided. The server treats a client that cannot distinguish subagents as one principal.
Only the fallback behaviour is pinned. No supported-client list exists.
Which local store and record schema, and how are migrations defined?
Answered in code by Prisma over SQLite or PostgreSQL, with a bootstrap that creates and migrates the store on first use. Not yet ratified as the decision.
Pinned as far as code can pin it: the bootstrap tests assert the schema this store ships with. Which store OFFCUT commits to is still an owner decision.
Maximum agents, record size, workspace size and context limit
Four numbers now exist in packages/core/src/types.ts and are enforced; the maximum size of a workspace is still not set anywhere.
Record text stops at 20 000 characters, one import at 1 000 records, a retrieval at 6 000 characters by default and 60 000 at most, and a workspace at 200 live agent keys — each refused in the same shape as the others, each justified in the code from something already in the system rather than chosen for looks. What the clause also asks for and still has no number is the maximum size of a workspace itself. The agent cap counts and inserts in separate statements, so two owners minting at the same instant can land a workspace one over its cap; that is deliberately harmless — an over-limit workspace keeps working and the next mint is refused — and deliberately written down.
Are model summaries needed in the first release, with which provider, and how is quality evaluated?
Undecided. The module exists, is off by default, and validates references rather than truth.
What is pinned is that the module is safe when off and honest when on. Whether it ships, and how its quality is judged, is open.
Backup cleanup timing, and restoration without resurrecting deleted data
Half answered: a restore replays deletions and retention defaults to 30 days. The policy itself is not ratified.
The 30-day default and the deletion ledger are pinned. The retention policy itself has not been agreed.
Package names, license and name/ticker clearance
Undecided. The @offcut npm organisation is reserved and nothing has been published; publishing is a team decision, not a specification requirement.
Unpinned because it is undecided, not because it is unfinished work: the @offcut organisation is reserved and nothing has been published, publishing is a team decision rather than a requirement of the specification, and a reservation is not a thing a test can assert.
no test pins this
Is there a paid service, how is $OFFCUT used, and what are the confirmed launch parameters?
Partly answered: on 2026-09-15 the owner gave the token a reward utility. Pricing, and the launch parameters including the contract address, are still open.
See the owner-decisions section for what was decided and what it deliberately overrides.
9. Optional Extension — Model Summaries 5 pinned
Not required for basic merging, and disabled by default
Nothing is sent anywhere until an owner turns it on, and only an owner may.
Output is labelled derived, cites source versions and passes structural validation
Invented citations are rejected and real ones kept, the block names the model that wrote it, and a deterministic block is never replaced by a model one.
The model gains no permission to change sources, resolve conflicts or reach a new audience
Whatever the model returns, nothing is changed, nobody's conflict is decided, and another agent's private records are never sent.
When the model fails, the linked records come back without a summary
A missing credential, a provider error or an unexpected shape costs the summary; writing, merging and retrieval carry on.
The provider credential never leaves the process
Not in an error, not in a report of a refusal, not in a quoted reply, and never as plaintext in the store.
Beyond the specification, which says nothing about credential storage. The tests exist because the module needs a credential.
Owner's decisions taken after v3.1 9 pinned
OPEN-7 partly closed: the token was given a utility on 2026-09-15
Section 5 forbade inventing a mechanic to fill the section. The owner lifted that ban deliberately and added a reward program, so the divergence is recorded here rather than discovered later.
The check against invented mechanics was replaced, not deleted: the page may describe this program and nothing else.
What is paid for: memory another agent actually retrieved, once in a record's life
A record credits its author when a different agent retrieves it, once however many retrievals or agents follow — a uniqueness constraint in the database, not a rule in code.
Superseded later the same week: retrievals are now a product statistic and confirmed AI spend is what pays. The mechanism still runs and is still tested.
What that cuts off: record spam, agent spam, repeat reads, reading your own records
Writing earns nothing however much of it there is, agents earn nothing by existing, and nobody is paid for reading themselves.
What it does not cut off, knowingly: one person with two agents
No metric resistant to farming exists while accounts are free; the money is protected by a daily ceiling, a per-earner cap, and rules that live off-chain and can change in a minute.
One accrual test measures the gap instead of closing it: a farm spread over five addresses is paid 2.77x what the same spend earns behind one.
Accepted consequence: a user with one agent earns nothing
One agent, however much it writes and reads back, earns nothing — and the earning starts the moment a second agent uses that memory.
Run as the scenario it actually is rather than as two halves: a single agent writes a week of records, reads every one of them back, and the total stays zero. A “solo bonus” added later to make that number less discouraging is what this would catch.
The wallet on the site is for receiving rewards, never for memory
Writing and reading still need no wallet and no network. The wallet is a second sign-in door and a payout address, and where you are paid is not who you are.
Invariant 10 still passes, which is the point: the core does not know a wallet exists.
The metric that pays is confirmed AI spend through a supported integration
An agent's claim earns nothing until the provider confirms the generation; one request counts once, every refusal is named with its reason, and spend nobody can confirm is reported as unconfirmed rather than as zero.
Distribution is onchain: a cumulative Merkle root, published per period, claimed once
A period settles once, the root only ever grows, a claim pays the difference and cannot be spent twice, and the publisher key can misdirect rewards but cannot take them.
Nothing has been deployed. The contract suite runs against a local chain and no distributor address is configured.
The disclosure the owner agreed to appears where the figure is
The contract is upgradeable, the owner can withdraw what is in it, this is not trustless, and none of it is required to use the memory system.
The files, by name every cited file once, with the tests it ran
packages/acceptance/src/acceptance.test.ts in @offcut/acceptance · 13 tests in the file
- passed Check 1 - two subagents save different results; a new lead agent queries the task returns one context with both results and source references
- passed Check 2 - retry a request; submit identical text from another agent writes no duplicate on retry and preserves the second note's provenance
- passed Check 3 - two values of one structured fact surfaces a visible conflict and overwrites neither version
- passed Check 3 - two values of one structured fact keeps both sides visible even when the budget is far too small
- passed Check 4 - concurrent writes and a restart keeps every acknowledged record and makes version conflicts explicit
- passed Check 5 - querying another workspace, or querying after revocation returns no content, no summary and no metadata
- passed Check 6 - delete a source after creating a summary returns neither the source nor its dependent block
- passed Check 7 - the same query through SDK and MCP returns the same context for identical permissions, data and settings
- passed Check 7 - the same query through SDK and MCP enforces the same permissions through both surfaces
- passed Check 7 - the same query through SDK and MCP writes through MCP and reads the result through the SDK
- passed Check 8 - no token and no external model connected still writes, merges, retrieves, deletes and exports
- passed PRIMARY acceptance check - a new agent continues the task from merged memory answers "what remains before release?" from three sessions it never attended
- passed PRIMARY acceptance check - a new agent continues the task from merged memory does not erase T1 when a later record says the bug was fixed
packages/core/src/__tests__/invariants.test.ts in @offcut/core · 26 tests in the file
- passed Invariant 1 - every record keeps workspace, author, source and version; merging never destroys sources stores authorship and evidence as separate fields
- passed Invariant 1 - every record keeps workspace, author, source and version; merging never destroys sources keeps the superseded version readable after a correction
- passed Invariant 1 - every record keeps workspace, author, source and version; merging never destroys sources leaves every source record intact after merging
- passed Invariant 2 - merging never broadens access keeps a private record out of a shared block even when the topic matches
- passed Invariant 2 - merging never broadens access never merges across workspaces
- passed Invariant 3 - a retried write creates no second record; identical text from another author keeps its provenance replays the stored result for the same idempotency key
- passed Invariant 3 - a retried write creates no second record; identical text from another author keeps its provenance rejects the same key carrying different content
- passed Invariant 3 - a retried write creates no second record; identical text from another author keeps its provenance keeps both authors when two agents save identical text
- passed Invariant 4 - acknowledged records survive restart; concurrent corrections never silently overwrite reads back every acknowledged record after reconnecting
- passed Invariant 4 - acknowledged records survive restart; concurrent corrections never silently overwrite rejects a correction built on a stale version
- passed Invariant 4 - acknowledged records survive restart; concurrent corrections never silently overwrite lets exactly one of two racing corrections win
- passed Invariant 5 - a detected conflict cannot vanish without a recorded resolution or deletion keeps reporting an open conflict on every recall
- passed Invariant 5 - a detected conflict cannot vanish without a recorded resolution or deletion closes it only through a recorded resolution that names the decision author
- passed Invariant 5 - a detected conflict cannot vanish without a recorded resolution or deletion reopens when a third value arrives that the decision never covered
- passed Invariant 6 - derived memory cites real source versions and a stale summary is never served as current pins a block to the exact versions it was built from
- passed Invariant 6 - derived memory cites real source versions and a stale summary is never served as current marks a block stale as soon as one of its sources changes
- passed Invariant 6 - derived memory cites real source versions and a stale summary is never served as current clears staleness only by rebuilding
- passed Invariant 7 - a deleted record never returns through search, export, cache or a dependent summary removes it from all four paths
- passed Invariant 7 - a deleted record never returns through search, export, cache or a dependent summary wipes the payload while keeping the tombstone honest
- passed Invariant 8 - every surface enforces the same access and mutation rules gives two equally-permissioned callers byte-identical context
- passed Invariant 8 - every surface enforces the same access and mutation rules applies permission flags identically no matter which operation is called
- passed Invariant 9 - memory text grants nothing, and revocation blocks what follows stores an injection attempt verbatim without granting it anything
- passed Invariant 9 - memory text grants nothing, and revocation blocks what follows blocks every later operation once access is revoked
- passed Invariant 10 - the local core runs without the token or an external model completes the full cycle with no wallet, chain or model configured
- passed Invariant 10 - the local core runs without the token or an external model stores an import claim without letting it become the verified author
- passed Cross-workspace isolation leaks nothing at all answers an outsider identically whether or not the workspace exists
packages/core/src/__tests__/spec-1-3-not-a-relayer.test.ts in @offcut/core · 4 tests in the file
- passed SS1.3 - OFFCUT stores, merges and retrieves memory itself runs the whole memory cycle with every outbound call trapped
- passed SS1.3 - OFFCUT stores, merges and retrieves memory itself holds the record in its own store rather than pointing at someone else
- passed SS1.3 - OFFCUT stores, merges and retrieves memory itself computes the merge itself, after the client has been thrown away
- passed SS1.3 - OFFCUT stores, merges and retrieves memory itself carries no outbound client anywhere in the memory path
apps/web/src/__tests__/token-section.test.tsx in @offcut/web · 18 tests in the file
- passed A configured value that is not an address is refused, explained, and not displayed
- passed A configured value that is not an address names where to fix it, because a warning with no address to go to is noise
- passed A configured value that is not an address stays silent when nothing is configured at all
- passed A configured value that is not an address stays silent once a real address is configured
- passed What the section is allowed to claim says the token role is undecided rather than inventing one
- passed What the section is allowed to claim promises no mechanic the specification leaves open
- passed Stock Rewards, as the brief requires it to be described calls the layer by its name and says once what it is
- passed Stock Rewards, as the brief requires it to be described says weight follows confirmed AI spend through supported integrations
- passed Stock Rewards, as the brief requires it to be described says one request is counted once
- passed Stock Rewards, as the brief requires it to be described says idle agents and a held balance both earn nothing
- passed Stock Rewards, as the brief requires it to be described says the fund is pre-funded and that no token is minted for anybody’s spend
- passed Stock Rewards, as the brief requires it to be described does not go on to claim that spending more only redivides the same pool
- passed Stock Rewards, as the brief requires it to be described says rewards do not guarantee that AI spend is recouped, and does not soften it
- passed Stock Rewards, as the brief requires it to be described says a cap exists, when it bites, and that what it withholds reaches nobody
- passed Stock Rewards, as the brief requires it to be described says the product is shared memory and this is the second layer
- passed Stock Rewards, as the brief requires it to be described names the one supported integration, and what spending outside it earns
- passed Stock Rewards, as the brief requires it to be described carries the disclosure the owner agreed to
- passed Stock Rewards, as the brief requires it to be described still refuses to trade anything
apps/web/src/__tests__/rewards-page.test.tsx in @offcut/web · 34 tests in the file
- passed The disclosure the owner agreed to says the contract is upgradeable
- passed The disclosure the owner agreed to says the owner can withdraw what is in it
- passed The disclosure the owner agreed to says in as many words that this is not trustless
- passed The disclosure the owner agreed to states what the contract does guarantee, so the warning is not noise
- passed The disclosure the owner agreed to says none of it is required to use the memory system
- passed The disclosure the owner agreed to says rewards do not guarantee that AI spend is recouped
- passed The disclosure the owner agreed to never tells a reader that spending more only redivides the same pool
- passed The name and what it means calls the layer Stock Rewards and defines it once
- passed The name and what it means says what decides weight, and what does not
- passed Confirmed spend shows what the provider confirmed, in dollars, and what it has not
- passed Confirmed spend reads it from the account, not from a workspace
- passed Confirmed spend says confirmed spend rather than implying an amount of a token that may not exist
- passed Confirmed spend names the cap on a busy period rather than letting it turn up in the payment
- passed Confirmed spend explains a zero that is really an unanswered question
- passed Confirmed spend values pending reports without promising them
- passed Confirmed spend says what was rejected and why, rather than dropping it
- passed Confirmed spend warns when nothing can be confirmed because no provider key is linked
- passed Confirmed spend says how many refusals came with no reason, rather than hiding them
- passed Confirmed spend names the supported integration and what spending outside it earns
- passed Confirmed spend says nothing about outstanding reports when there are none
- passed The old metric, kept as a statistic shows retrievals the API counted, across every workspace
- passed The old metric, kept as a statistic says plainly that it no longer decides money
- passed The old metric, kept as a statistic still explains what counts, so nobody plans a farm around writing records
- passed The old metric, kept as a statistic says plainly when nothing has been retrieved
- passed What the page leads with leads with confirmed AI spend and not with retrievals
- passed What the page leads with shows the confirmed total the server counted, in dollars
- passed What the page leads with marks the retrieval count as a statistic rather than as money
- passed A workspace nobody can be asked about says no key is linked where its figure would be, instead of a zero
- passed A workspace nobody can be asked about does not spend the alert crimson on it
- passed A workspace nobody can be asked about puts the form that would fix it on the same page, once per workspace
- passed A workspace nobody can be asked about says nothing can be confirmed yet when no workspace anywhere has a key
- passed A workspace nobody can be asked about still calls confirmed spend confirmed after its key is unlinked
- passed The sentences the brief insists on carries all four in the panel the figure is in, not somewhere further down
- passed The sentences the brief insists on sells nothing while it says them
apps/web/src/__tests__/wallet-modal.test.ts in @offcut/web · 5 tests in the file
- passed the wallet modal offers no way to send a transaction
- passed the wallet modal offers no way to swap
- passed the wallet modal offers no way to buy or fund
- passed the wallet modal offers no transaction history
- passed the wallet modal draws nothing at all in the row those buttons live in
packages/acceptance/src/spec-surface-inventory.test.ts in @offcut/acceptance · 7 tests in the file
- passed SS1.4 - the product has nowhere to trade anything offers exactly the memory tools of SS4.1, plus usage reporting, and nothing else
- passed SS1.4 - the product has nowhere to trade anything names every tool for memory or for usage, never for a market
- passed SS1.4 - the product has nowhere to trade anything carries no trading vocabulary in any name, description or parameter
- passed SS1.4 - the product has nowhere to trade anything asks for no wallet, key or chain anywhere in its parameters
- passed SS3.1 - no tool can be pointed at something to ingest takes no path, file, folder, url or chat export as a parameter
- passed SS3.1 - no tool can be pointed at something to ingest makes the import carry its own records, so nothing is fetched on its behalf
- passed SS3.1 - no tool can be pointed at something to ingest tells the agent, in the description it reads, that connecting reads nothing
apps/api/src/__tests__/api.e2e.test.ts in @offcut/api · 74 tests in the file
- passed Health is public and reports that the core needs no token or model
- passed Authentication refuses every protected route without a credential
- passed Authentication registers, signs in and returns the session as an httpOnly cookie
- passed Authentication gives the same answer for a wrong password and an unknown account
- passed Authentication rate limits sign-in attempts
- passed Agent keys are the identity authenticates an agent by Bearer key and attributes its writes
- passed Agent keys are the identity ignores an agentId supplied in the request body
- passed Agent keys are the identity blocks a revoked key on the very next request
- passed Permissions are enforced at this layer too refuses operations the agent was not granted
- passed Permissions are enforced at this layer too stops an agent creating another agent
- passed Cross-workspace isolation returns no content, summary or metadata from a workspace you cannot see
- passed Cross-workspace isolation answers identically for a foreign workspace and one that does not exist
- passed Installation-wide snapshots are not on the HTTP surface gives a signed-in account no route to the installation snapshot at all
- passed All eight operations over HTTP runs the full cycle and keeps the merge rules intact
- passed All eight operations over HTTP replays an idempotent write instead of duplicating it
- passed All eight operations over HTTP refuses a correction built on a stale version
- passed Pagination pages without ever reporting more than the caller may see
- passed Pagination filters by search text
- passed Workspace administration updates settings and deletes only for the owner
- passed Workspace administration shows an agent key exactly once
- passed The payout address is stored in the single form it will be paid in
- passed The payout address refuses anything that is not an address instead of storing it
- passed The payout address refuses the zero address, which is well formed and unrecoverable
- passed The payout address can be replaced, and clearing it loses nothing that was earned
- passed The payout address belongs to a person, so an agent key cannot set one
- passed The payout address is invisible to another account
- passed Retrievals credited: the product statistic, which is not the payout counts a record when another agent retrieves it, and counts it once
- passed Retrievals credited: the product statistic, which is not the payout does not credit an owner for reading their own workspace
- passed The rewards summary answers with the figure that actually pays reports confirmed AI spend, not the records another agent retrieved
- passed The rewards summary answers with the figure that actually pays reports spend as whole millionths of a dollar and never as dollars
- passed The rewards summary answers with the figure that actually pays pays on nothing the provider has not answered for
- passed The rewards summary answers with the figure that actually pays names the reports that earn nothing, and the reason each was refused
- passed The rewards summary answers with the figure that actually pays answers with nothing outstanding rather than omitting the question
- passed The rewards summary answers with the figure that actually pays counts only the reports of workspaces this person owns
- passed The rewards summary answers with the figure that actually pays totals every workspace the person owns, in one answer
- passed The rewards summary answers with the figure that actually pays shows a workspace with no key linked instead of hiding its zero
- passed The rewards summary answers with the figure that actually pays never lets the stored credential out, in any form
- passed The rewards summary answers with the figure that actually pays answers an account with no workspaces with a zero it can explain
- passed The rewards summary answers with the figure that actually pays counts no spend belonging to another account
- passed The provider credential links a key, reports it as four characters, and clears it again
- passed The provider credential never returns more of the key than its hint, on any route or any failure
- passed The provider credential stores nothing when OpenRouter does not recognise the key
- passed The provider credential is the owner alone: another account and the workspace own agent are refused
- passed Reporting confirmed AI spend takes reports from an agent and refuses them from a signed-in person
- passed Reporting confirmed AI spend counts one request once however many times it is reported
- passed Reporting confirmed AI spend refuses a batch larger than the core will accept
- passed Reporting confirmed AI spend rate limits an agent reporting in a loop
- passed Reporting confirmed AI spend will not let an agent report spend into another workspace
- passed Watching a workspace work says where the trail stands before it says anything else
- passed Watching a workspace work stops reading the database the moment the client goes away
- passed Watching a workspace work drains a burst rather than dripping it out a page a second
- passed Watching a workspace work keeps a silent connection alive with a comment
- passed Watching a workspace work refuses an agent key, with a body rather than a silent open socket
- passed Watching a workspace work refuses a signed-in person who does not own the workspace
- passed Watching a workspace work answers for a workspace that does not exist exactly as it answers for one that is not yours
- passed Watching a workspace work rate limits a client that reconnects in a loop
- passed Sign-In-With-Ethereum: a second door, never a gate signs in a wallet nobody has seen before and opens an account with no email
- passed Sign-In-With-Ethereum: a second door, never a gate gives the wallet door the same session the email door gives
- passed Sign-In-With-Ethereum: a second door, never a gate signs into the account that linked this address by hand rather than opening a second one
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a nonce this browser was never given
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a captured sign-in request replayed by a client that ignores Set-Cookie
- passed Sign-In-With-Ethereum: a second door, never a gate spends the nonce on a refusal too, so being caught out buys no second go
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a signature made for another domain
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a message naming a chain this server does not sign in on
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a message that has expired
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a message signed longer ago than a sign-in takes
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a signature made by a different key
- passed Sign-In-With-Ethereum: a second door, never a gate refuses a message carrying a statement this service never wrote
- passed Sign-In-With-Ethereum: a second door, never a gate refuses something that is not a Sign-In-With-Ethereum message at all
- passed Sign-In-With-Ethereum: a second door, never a gate refuses rather than trusts the signature when the chain cannot be reached
- passed Sign-In-With-Ethereum: a second door, never a gate will not let a wallet-only account be reached with any password
- passed Sign-In-With-Ethereum: a second door, never a gate rate limits wallet sign-in attempts
- passed Sign-In-With-Ethereum: a second door, never a gate hands out the nonce in an httpOnly cookie that is not the nonce
- passed Picking the stream back up where it dropped replays what was appended while the client was away, and refuses a cursor that is not a time
packages/core/src/__tests__/validation-errors.test.ts in @offcut/core · 8 tests in the file
- passed A rejected input is reported as VALIDATION on every surface turns a missing required field into an OffcutError, not a zod error
- passed A rejected input is reported as VALIDATION on every surface names the offending field, so a model can act on the message
- passed A rejected input is reported as VALIDATION on every surface rejects an unknown record type the same way
- passed A rejected input is reported as VALIDATION on every surface covers every operation, not just add
- passed The translation itself recognises a zod error by shape rather than by class
- passed The translation itself recognises an OffcutError by shape rather than by class
- passed The translation itself passes a non-zod failure through untouched
- passed The translation itself returns the parsed value when the input is good
packages/core/src/__tests__/spec-2-4-context-within-the-limit.test.ts in @offcut/core · 4 tests in the file
- passed SS2/SS3.4 - context comes back inside the budget it was given spends no more characters than the caller allowed, and says it stopped
- passed SS2/SS3.4 - context comes back inside the budget it was given stops at the number of items the caller asked for
- passed SS2/SS3.4 - context comes back inside the budget it was given loses nothing from the store when an answer is shortened
- passed SS2/SS3.4 - context comes back inside the budget it was given never spends the budget on records the caller may not see
packages/core/src/__tests__/agent-limit.test.ts in @offcut/core · 8 tests in the file
- passed A workspace holds at most LIMITS.agentsPerWorkspaceMax agents mints the agent that reaches the limit and refuses the one after it
- passed A workspace holds at most LIMITS.agentsPerWorkspaceMax agents refuses in the shape every other limit refuses in
- passed A workspace holds at most LIMITS.agentsPerWorkspaceMax agents counts only keys that can still connect: a revoked one frees its seat
- passed A workspace holds at most LIMITS.agentsPerWorkspaceMax agents does not charge the owner for the console writer the store provisions
- passed A workspace already over the limit keeps working lists every agent, keeps writing and recalling, and only refuses new keys
- passed The three older OPEN-3 limits are enforced, not just declared refuses a record longer than LIMITS.recordTextMax
- passed The three older OPEN-3 limits are enforced, not just declared refuses an import batch larger than LIMITS.importBatchMax
- passed The three older OPEN-3 limits are enforced, not just declared refuses a retrieval budget larger than LIMITS.contextLimitMax
packages/core/src/__tests__/spec-3-1b-nothing-enters-by-itself.test.ts in @offcut/core · 3 tests in the file
- passed SS3.1 - connecting is not access to anything leaves the workspace empty after every agent has connected
- passed SS3.1 - connecting is not access to anything stays empty until a record is handed over on purpose
- passed SS3.1 - connecting is not access to anything refuses the import to an agent that was not granted it
apps/web/src/__tests__/api.test.ts in @offcut/web · 38 tests in the file
- passed A failed request carries the server’s own words surfaces the message the API sent rather than a generic one
- passed A failed request carries the server’s own words keeps the status, so the console can tell "signed out" from "broken"
- passed A failed request carries the server’s own words falls back to a generic message only when the server sent none
- passed A failed request carries the server’s own words does not mistake an empty error body for a successful call
- passed A failed request carries the server’s own words is a real Error, so it survives logging and rethrowing
- passed A failure with no usable response is still an ApiError handles the API process simply not being there
- passed A failure with no usable response is still an ApiError handles a connection dropped midway through the body
- passed A failure with no usable response is still an ApiError handles an HTML error page from something in front of the API
- passed A failure with no usable response is still an ApiError handles a 200 whose body is not JSON either
- passed A failure with no usable response is still an ApiError accepts an empty body on a successful call
- passed Every request is shaped the way the API expects sends the session cookie
- passed Every request is shaped the way the API expects goes to /api on this origin, not to a cross-origin host
- passed Every request is shaped the way the API expects declares a JSON content type
- passed Every request is shaped the way the API expects reads with GET and no body
- passed Every request is shaped the way the API expects posts JSON
- passed Every request is shaped the way the API expects sends an empty object rather than nothing on a bodyless POST
- passed Every request is shaped the way the API expects patches partial updates instead of replacing the resource
- passed Every request is shaped the way the API expects deletes with DELETE
- passed Every request is shaped the way the API expects never makes a request a test did not arrange
- passed Writes are replay-safe mints a fresh key per action
- passed Writes are replay-safe labels the key with the action it belongs to
- passed Writes are replay-safe attaches one to an import
- passed Writes are replay-safe attaches one to a conflict resolution
- passed Writes are replay-safe passes a caller-supplied key through untouched on add
- passed Record queries always sends a bounded page, so a large workspace cannot be requested whole
- passed Record queries carries the caller’s paging
- passed Record queries escapes a search term rather than splicing it into the URL
- passed Record queries omits empty filters instead of sending blank ones
- passed The rewards summary is read from the account, not from one workspace
- passed The rewards summary hands the console the confirmed figure exactly as the server counted it
- passed A zero in the spend list is never left to explain itself reads a zero with no key linked as nobody having been asked
- passed A zero in the spend list is never left to explain itself reads a zero with a key linked as nothing confirmed yet
- passed A zero in the spend list is never left to explain itself still calls confirmed spend confirmed after the key is unlinked
- passed A zero in the spend list is never left to explain itself calls a workspace with both spend and a key confirmed
- passed The wallet door asks for a nonce with a plain GET, and sends the cookie jar with it
- passed The wallet door posts the message and the signature, and nothing else
- passed The wallet door hands back an account with no email rather than inventing one
- passed The wallet door carries the server’s own refusal when the chain could not be reached
packages/core/src/__tests__/spec-3-3c-uncertain-relationships.test.ts in @offcut/core · 4 tests in the file
- passed SS3.3 row 5 - an uncertain relationship leaves records separate keeps two near-identical sentences as two items, collapsing neither
- passed SS3.3 row 5 - an uncertain relationship leaves records separate flags no conflict between two contradictory sentences in free text
- passed SS3.3 row 5 - an uncertain relationship leaves records separate counts unrelated records as kept-separate instead of joining them
- passed SS3.3 row 5 - an uncertain relationship leaves records separate does act when the relationship is stated explicitly
packages/core/src/__tests__/summaries.test.ts in @offcut/core · 25 tests in the file
- passed Off by default (SS9, SS4.2) sends nothing anywhere until an owner turns it on
- passed Off by default (SS9, SS4.2) reports the setting without ever exposing the credential
- passed Only an owner may authorise it (SS4.2) refuses an agent, including one with every memory permission
- passed Only an owner may authorise it (SS4.2) records who granted it and when
- passed Failure costs a summary and nothing else (SS9) returns the records unsummarised when no credential is configured
- passed Failure costs a summary and nothing else (SS9) returns the records unsummarised when the provider errors
- passed Failure costs a summary and nothing else (SS9) returns the records unsummarised when the reply is not the expected shape
- passed Failure costs a summary and nothing else (SS9) leaves writing, merging and retrieval working after a failure
- passed Structural validation of citations (SS9) keeps real citations and rejects invented ones
- passed Structural validation of citations (SS9) labels the stored block as derived and names the model
- passed Structural validation of citations (SS9) never replaces a deterministic block with a model one
- passed The model gains no authority (SS9) cannot change a record or resolve a conflict, whatever it returns
- passed The model gains no authority (SS9) never sends another agent’s private records to the provider
- passed The model gains no authority (SS9) tells the model that record text is data, not instructions
- passed Tolerant of field naming, strict about ids accepts alternative field names a model actually produces
- passed Tolerant of field naming, strict about ids still rejects invented ids under any field name
- passed Tolerant of field naming, strict about ids drops a record related to itself
- passed The window during the provider call (found by adversarial review) does not resurrect a record deleted while the model was thinking
- passed The window during the provider call (found by adversarial review) marks the block stale when a source was corrected mid-call
- passed The window during the provider call (found by adversarial review) still clears staleness when nothing moved during the call
- passed Deterministic and model blocks never touch each other a deterministic merge does not overwrite the model block
- passed Deterministic and model blocks never touch each other a summary does not overwrite the deterministic block
- passed The server credential never leaves the process says nothing about the key when a control character makes the header unbuildable
- passed The server credential never leaves the process reports the status of a refused call without the body that came with it
- passed The server credential never leaves the process names an unreadable reply rather than quoting it
packages/core/src/__tests__/credits.test.ts in @offcut/core · 11 tests in the file
- passed Memory another agent used credits the author when someone else retrieves their record
- passed Memory another agent used pays once however many times the record is retrieved
- passed Memory another agent used pays once however many different agents retrieve it
- passed What earns nothing writing, however much of it
- passed What earns nothing reading your own records back
- passed What earns nothing creating agents
- passed What earns nothing an owner reading their own workspace in the console
- passed What earns nothing a recall that returned nothing
- passed What earns nothing a record the reader was never allowed to see
- passed Windows, for paying by period counts only what was earned inside the window
- passed Windows, for paying by period never counts a row in two consecutive windows
packages/core/src/__tests__/spec-3-4c-empty-not-invented.test.ts in @offcut/core · 4 tests in the file
- passed SS3.4 step 4 - no data means an empty result returns nothing at all from an empty workspace
- passed SS3.4 step 4 - no data means an empty result returns nothing when every record belongs to another agent privately
- passed SS3.4 step 4 - no data means an empty result returns nothing after the only record is deleted
- passed SS3.4 step 4 - no data means an empty result says so, and returns stored text, when the question matches nothing
packages/core/src/__tests__/backup.test.ts in @offcut/core · 9 tests in the file
- passed Snapshots captures every table and reports what it wrote
- passed Snapshots restores a workspace that was wiped
- passed Restoring must not resurrect a deleted record (invariant 7) replays deletions recorded after the snapshot was taken
- passed Restoring must not resurrect a deleted record (invariant 7) is idempotent — restoring twice is safe
- passed Restoring must not resurrect a deleted record (invariant 7) does not replay a deletion that happened BEFORE the snapshot
- passed Retention defaults to 30 days
- passed Retention prunes snapshots past the window and keeps the rest
- passed Retention never prunes the deletion ledger
- passed Retention records identity but never content
packages/core/src/__tests__/bootstrap.test.ts in @offcut/core · 14 tests in the file
- passed An empty store gets its tables before the first query fails exactly as the consumer saw without the bootstrap
- passed An empty store gets its tables before the first query answers the first query on a brand-new file
- passed An empty store gets its tables before the first query works inside an interactive transaction as the very first operation
- passed An empty store gets its tables before the first query is idempotent, so a second process opening the same file is harmless
- passed An empty store gets its tables before the first query creates every table the schema declares, not just the one it checks for
- passed An empty store gets its tables before the first query gives a store from an older release the table it never had
- passed An empty store gets its tables before the first query stamps the file when it is done, and trusts the stamp afterwards
- passed An empty store gets its tables before the first query derives its table list from the shipped DDL, not from a hand-written array
- passed An empty store gets its tables before the first query leaves a PostgreSQL store untouched
- passed A store that is not empty refuses a database that belongs to something else
- passed A store that is not empty finishes a bootstrap that was cut short
- passed A store that is not empty leaves a complete, stamped store alone
- passed A store from an earlier version is brought up to shape, not broken adds the column a newer unique index needs, and keeps the rows
- passed A store from an earlier version is brought up to shape, not broken refuses a store whose users table still forbids a null email, and says how to migrate
packages/core/src/__tests__/db.test.ts in @offcut/core · 6 tests in the file
- passed Which variable decides OFFCUT_DATABASE_URL wins over everything, installed or not
- passed Which variable decides an installed copy ignores the generic DATABASE_URL
- passed Which variable decides a checkout honours DATABASE_URL, resolved the way the Prisma CLI resolves it
- passed Relative paths resolves a relative OFFCUT_DATABASE_URL against the working directory
- passed Relative paths leaves absolute and non-file URLs alone
- passed The environment is read, never written building the client leaves DATABASE_URL exactly as it was
apps/web/src/__tests__/operations.test.tsx in @offcut/web · 9 tests in the file
- passed The eight operations, as the page states them names the permission the code actually requires, for every operation
- passed The eight operations, as the page states them invents no permission and skips no operation
- passed The eight operations, as the page states them gives every tile a description, so no tile is a bare label
- passed The call each tile shows when you point at it names a method the SDK really exposes, for every operation
- passed The call each tile shows when you point at it passes arguments the input schema really accepts, for every operation
- passed The hover, which is CSS and nothing else gives every tile the classnames and the tabstop the stylesheet needs
- passed The hover, which is CSS and nothing else keeps both faces of the band in the document, so neither is rendered by an event
- passed The hover, which is CSS and nothing else declares the lift, the sweep and the mint edge in globals.css
- passed The hover, which is CSS and nothing else takes the movement away under prefers-reduced-motion, and leaves the colour
packages/core/src/__tests__/spec-4-2c-local-store-trust-boundary.test.ts in @offcut/core · 3 tests in the file
- passed SS4.2 - where the SDK protects the store, and where it stops holds the line on its own path: no reader reaches another agent private record
- passed SS4.2 - where the SDK protects the store, and where it stops cannot stop a writer that goes straight at the database
- passed SS4.2 - where the SDK protects the store, and where it stops leaves no trace of that write in the audit trail
apps/web/src/__tests__/token.test.ts in @offcut/web · 33 tests in the file
- passed A valid address is accepted accepts 0x followed by 40 hex characters
- passed A valid address is accepted accepts both letter cases, because checksummed addresses are mixed case
- passed A valid address is accepted tolerates the whitespace a copy-paste drags along
- passed A valid address is accepted reports the token as launched once the address is set
- passed A malformed address is refused, never displayed refuses one character short
- passed A malformed address is refused, never displayed refuses one character long
- passed A malformed address is refused, never displayed refuses a missing 0x prefix
- passed A malformed address is refused, never displayed refuses a non-hex character
- passed A malformed address is refused, never displayed refuses an uppercase 0X prefix no wallet emits
- passed A malformed address is refused, never displayed refuses an address still wrapped in quotes
- passed A malformed address is refused, never displayed refuses a Solana address pasted onto an EVM chain
- passed A malformed address is refused, never displayed refuses a placeholder someone left behind
- passed A malformed address is refused, never displayed refuses the bare prefix
- passed A malformed address is refused, never displayed never builds an explorer link for a malformed address
- passed An empty address is the "not yet announced" state is not launched when nothing has been pasted
- passed An empty address is the "not yet announced" state does not call an empty slot malformed
- passed An empty address is the "not yet announced" state treats whitespace as unset rather than as a value
- passed An empty address is the "not yet announced" state offers no explorer link before launch
- passed The environment override publishes an address supplied by the deployment
- passed The environment override falls back to the pre-launch state when the variable is unset
- passed The environment override refuses a malformed value from the environment too
- passed The environment override carries the explorer and venue URLs through as well
- passed Explorer links joins the base and the address without doubling the slash
- passed Explorer links adds the missing slash when the base does not end in one
- passed Explorer links links the trimmed address, never the pasted whitespace
- passed Explorer links stays null while the explorer URL is unknown
- passed Display truncation keeps the head and tail a reader actually checks
- passed Display truncation honours custom lead and tail lengths
- passed Display truncation leaves short values alone rather than truncating them to nonsense
- passed Display truncation trims before measuring
- passed Display truncation is a display form only - it is never itself a valid address
- passed Token identity keeps the symbol and the ticker in step
- passed Token identity publishes a chain alongside the address
apps/web/src/__tests__/contract-address.test.tsx in @offcut/web · 35 tests in the file
- passed Before launch it says so, and shows nothing else states plainly that there is no address yet
- passed Before launch it says so, and shows nothing else renders nothing that could be mistaken for an address
- passed Before launch it says so, and shows nothing else offers no copy button, because there is nothing to copy
- passed Before launch it says so, and shows nothing else warns that addresses found elsewhere are unverified
- passed Before launch it says so, and shows nothing else renders nothing at all in the compact variant
- passed Before launch it says so, and shows nothing else refuses to display a malformed address that reached the config
- passed Before launch it says so, and shows nothing else refuses a malformed address in the compact variant too
- passed After launch it shows the address in full prints the complete string, not a truncation
- passed After launch it shows the address in full names the chain alongside it
- passed After launch it shows the address in full shows the truncated form in the compact variant
- passed After launch it shows the address in full keeps the full address reachable from the compact variant
- passed Copy yields the full address, never the shortened one copies the whole string from the full variant
- passed Copy yields the full address, never the shortened one copies the whole string from the compact variant, not what is displayed
- passed Copy yields the full address, never the shortened one copies the trimmed value when the config has stray whitespace
- passed Copy yields the full address, never the shortened one confirms the copy to the user
- passed Copy yields the full address, never the shortened one goes back to offering a copy, so a second copy is possible
- passed Copy yields the full address, never the shortened one survives a clipboard the browser refuses
- passed Outbound links shows no explorer link while the explorer URL is unknown
- passed Outbound links points the explorer link at this exact address
- passed Outbound links shows the venue link only when a venue URL is configured
- passed The address arrives while the page is open, with no rebuild publishes the address the server announces
- passed The address arrives while the page is open, with no rebuild asks for it uncached, because a cached answer is a launch nobody sees
- passed The address arrives while the page is open, with no rebuild says "not yet published" until the server has one
- passed The address arrives while the page is open, with no rebuild copies the served address in full
- passed The address arrives while the page is open, with no rebuild links the explorer the server named
- passed The address arrives while the page is open, with no rebuild names the chain the server named, rather than the one in the bundle
- passed The address arrives while the page is open, with no rebuild drops the chain id when the server names a chain this build does not know
- passed The address arrives while the page is open, with no rebuild keeps the chain id when the server names the chain it knows
- passed The address arrives while the page is open, with no rebuild refuses a malformed address even from its own server
- passed The address arrives while the page is open, with no rebuild stays pre-launch when the server reports a broken file
- passed The address arrives while the page is open, with no rebuild shows an address published after the page was already open
- passed The address arrives while the page is open, with no rebuild keeps a published address when a later request fails
- passed The address arrives while the page is open, with no rebuild does not ask while the tab is in the background
- passed The address arrives while the page is open, with no rebuild asks again the moment the tab comes back
- passed The address arrives while the page is open, with no rebuild stops asking once it is off the page
packages/core/src/__tests__/wallet-payout-vs-identity.test.ts in @offcut/core · 4 tests in the file
- passed Where you are paid is not who you are still opens to its own key after the payout address is pointed elsewhere
- passed Where you are paid is not who you are still opens to its own key after the payout address is forgotten
- passed Where you are paid is not who you are stops calling the payout address proven the moment it differs from the key
- passed Where you are paid is not who you are never lets typing an address write the sign-in column
packages/rewards/src/__tests__/accrual.test.ts in @offcut/rewards · 35 tests in the file
- passed The ceiling on a busy period never allocates more than the period share of the daily ceiling
- passed The ceiling on a busy period splits the pool in proportion to points
- passed The ceiling on a busy period leaves the division remainder in the contract rather than giving it to whoever sorts first
- passed The rate on a quiet period pays the rate per point and mints nothing more
- passed The rate on a quiet period pays a lone earner the whole of a pool that only their own spending made
- passed The rate on a quiet period pays two earners their own dollar each rather than half of it
- passed The rate on a quiet period divides exactly when the rate decides the pool, so nothing is stranded
- passed The cap on one earner holds a dominant workspace to its share of the pool
- passed The cap on one earner is not beaten by splitting a farm across several workspaces
- passed The cap on one earner leaves a dominant earner alone while the rate is what priced the period
- passed The cap on one earner closes on that same earner once the ceiling is what is being divided
- passed The cap on one earner leaves what the cap removed in the contract
- passed The cap on one earner names a payee it held at the cap even when one workspace carried all their spend
- passed What the cap does not do holds a farm to a quarter of the period while its spend sits behind one address
- passed What the cap does not do pays that same farm 2.77x more once it spreads the same spend over five addresses
- passed What the cap does not do pays the honest earners the same either way, because the pool is what is fixed
- passed Earners nobody can pay pays a workspace with no wallet nothing and reports it by name
- passed Earners nobody can pay does not hand their share to everybody else
- passed Earners nobody can pay refuses a stored wallet that is not an address instead of guessing at it
- passed Windows it will not price refuses a window that is not exactly one period long
- passed Windows it will not price decides nothing for a period in which nothing was earned
- passed Windows it will not price ignores spend confirmed outside the window
- passed The metric is confirmed AI spend pays for confirmed model spend
- passed The metric is confirmed AI spend pays nothing for memory another agent retrieved
- passed The metric is confirmed AI spend divides the pool by spend alone when a workspace has both
- passed The metric is confirmed AI spend pays nothing for spend no provider has confirmed yet
- passed The metric is confirmed AI spend pays nothing for spend a provider refused to recognise
- passed The metric is confirmed AI spend earns a workspace of idle agents nothing at all
- passed Realistic magnitudes prices a dollar of confirmed spend at one token while the ceiling is clear
- passed Realistic magnitudes splits thousands of dollars across several workspaces without rounding badly
- passed Realistic magnitudes holds a large spender to the same cap as a small one
- passed Realistic magnitudes still pays a spender a million times smaller than its neighbour
- passed Realistic magnitudes refuses to price a window whose total it cannot add up exactly
- passed What one more dollar of confirmed spend does to the period adds to what the period pays out while the ceiling is clear, and takes nothing from the people already in it
- passed What one more dollar of confirmed spend does to the period stops adding once the ceiling binds, and shrinks everybody already in the period
packages/core/src/__tests__/secrets.test.ts in @offcut/core · 14 tests in the file
- passed A stored credential survives a round trip comes back exactly as it went in
- passed A stored credential survives a round trip never stores the plaintext
- passed A stored credential survives a round trip produces a different ciphertext every time the same value is encrypted
- passed A stored credential survives a round trip accepts a key written as hex as well as base64
- passed The wrong key fails rather than returning garbage refuses to decrypt under a different OFFCUT_SECRET_KEY
- passed The wrong key fails rather than returning garbage refuses a ciphertext whose bytes were edited in the row
- passed The wrong key fails rather than returning garbage refuses a ciphertext belonging to another workspace
- passed The wrong key fails rather than returning garbage refuses a value that is not in a format it recognises
- passed A misconfigured server refuses instead of falling back will not encrypt without OFFCUT_SECRET_KEY, and says how to make one
- passed A misconfigured server refuses instead of falling back will not accept a key of the wrong length
- passed A misconfigured server refuses instead of falling back will not accept a passphrase that merely looks like base64
- passed A misconfigured server refuses instead of falling back never puts the configured value into the error
- passed The hint identifies a key without being one is the last four characters and nothing else
- passed The hint identifies a key without being one is empty for a value too short to hint at safely
packages/rewards/src/__tests__/config.test.ts in @offcut/rewards · 10 tests in the file
- passed The defaults are usable as they stand
- passed The defaults spread the ceiling across the day, erring below it and never above
- passed The defaults hold one earner to a quarter of a period
- passed Reading settings from the environment takes amounts in whole tokens and stores base units
- passed Reading settings from the environment falls back to the defaults when nothing is set
- passed Reading settings from the environment refuses more precision than the token has, rather than rounding it away
- passed Reading settings from the environment refuses a negative ceiling
- passed Reading settings from the environment refuses a period that does not tile a day
- passed Reading settings from the environment refuses a cap that caps nothing
- passed Reading settings from the environment refuses a cap finer than a basis point
packages/core/src/__tests__/spec-own-5-single-agent-earns-nothing.test.ts in @offcut/core · 2 tests in the file
- passed The accepted consequence - one agent earns nothing earns nothing however much the single agent writes and reads back
- passed The accepted consequence - one agent earns nothing starts earning the moment a second agent uses that memory
packages/core/src/__tests__/wallet-identity.test.ts in @offcut/core · 14 tests in the file
- passed One address is one account opens an account the first time an address signs in, and finds it ever after
- passed One address is one account opens one account, not two, when a new address signs in from two tabs at once
- passed One address is one account keeps what the second tab made reachable, because the second tab is the same account
- passed One address is one account reaches the hand-linked account even when two sign-ins race for the same address
- passed One address is one account reaches one fixed account when two accounts of the same age name the same address
- passed One address is one account finds the same account when the address arrives in a different casing
- passed One address is one account names a wallet-only account after its address and gives it no email or password
- passed One address is one account signs into the account that linked this address by hand, rather than opening a second one
- passed One address is one account records that a signature proved an address a hand link only claimed
- passed One address is one account refuses the zero address, which nobody holds the key to
- passed A wallet account has no password to guess refuses a password login with the same sentence a wrong password gets
- passed A wallet account has no password to guess refuses an empty password against an account whose passwordHash is null
- passed Nothing that leaves this module carries a secret returns no passwordHash from the wallet door
- passed Nothing that leaves this module carries a secret resolves a wallet-only session into a principal with a null email
apps/web/src/__tests__/login-doors.test.tsx in @offcut/web · 23 tests in the file
- passed Two doors, wallet first offers the wallet above the email form, and both on one sheet
- passed Two doors, wallet first keeps register mode for the email door
- passed Two doors, wallet first says the email door reaches the same console, so nobody reads it as second best
- passed What the paper says before anything is signed says the signature is free and moves nothing
- passed What the paper says before anything is signed says what the signature is actually for
- passed What the paper says before anything is signed promises no transaction is ever asked for
- passed What the paper says before anything is signed prints the spec sentence: no wallet, no gas, no token purchase
- passed Signing in with a wallet asks for a nonce, signs the server’s own sentence, and posts both
- passed Signing in with a wallet opens the console once the server has accepted the signature
- passed Signing in with a wallet never asks a disconnected visitor to sign anything
- passed Signing in with a wallet shows the server’s own words when the chain could not be consulted
- passed When the reader declines says plainly that nothing happened
- passed When the reader declines does not ask a second time on its own, even when the wallet reconnects
- passed When the reader declines offers to try again, and asks again only when asked
- passed When the reader declines does not show a refusal as an error, because it is not one
- passed When the wallet is on the wrong network names the network instead of collecting a signature the server would refuse
- passed When the wallet is on the wrong network offers the switch, and switches to the chain the server checks
- passed When the wallet is on the wrong network says nothing has been signed, so the state is not left to guesswork
- passed The demo account is filled in on a development machine
- passed The demo account is nowhere on the page in production
- passed One plate on the sheet gives the fill to Connect wallet and to nothing else
- passed One plate on the sheet leaves the email submit in the quieter weight
- passed One plate on the sheet keeps it to one plate once a wallet is connected and waiting to sign
apps/web/src/__tests__/link-wallet.test.tsx in @offcut/web · 12 tests in the file
- passed What the screen says linking means states that connecting proves an address, not ownership of it
- passed What the screen says linking means says that anyone signed in to the account could point rewards elsewhere
- passed What the screen says linking means does not claim a signature was checked, because none was
- passed The address on file is shown in full, so it can be checked character by character
- passed The address on file says when it was set
- passed The address on file is described as unset when there is none, without inventing a placeholder
- passed Linking the connected wallet sends the same string it shows, in the form the server stores
- passed Linking the connected wallet offers to replace an address that is already on file
- passed Linking the connected wallet treats the same wallet in another case as already linked
- passed Linking the connected wallet shows the server’s refusal rather than a shrug
- passed Unlinking clears the address and says so to the rest of the page
- passed Unlinking is not offered when there is nothing to unlink
packages/core/src/__tests__/usage.test.ts in @offcut/core · 31 tests in the file
- passed Spend is confirmed, never claimed earns nothing until a provider agrees
- passed Spend is confirmed, never claimed pays what the provider says, not what the agent claimed
- passed Spend is confirmed, never claimed rejects a request the provider does not recognise, and says so
- passed Spend is confirmed, never claimed keeps a report pending when the provider cannot be reached
- passed Spend is confirmed, never claimed a human in the console cannot report spend
- passed One request is counted once ignores the same generation reported twice
- passed One request is counted once ignores it when a second agent claims the same request
- passed One request is counted once writes one row for an id repeated inside a single batch
- passed One request is counted once keeps the rest of a batch when one id is claimed between the read and the write
- passed One request is counted once pays a verified request once even if verification runs again
- passed What earns nothing a hundred idle agents
- passed What earns nothing a malformed generation id
- passed What earns nothing a provider we cannot ask
- passed What earns nothing a negative cost, which cannot subtract from anyone
- passed What earns nothing an impossible token count, which cannot fail the reports sent with it
- passed Windows, for paying by period counts confirmed spend inside the window and nothing outside it
- passed Windows, for paying by period windows on when it was confirmed, not when it was reported
- passed A rejection does not get to own the id gives the id back to the workspace that actually paid for it
- passed A rejection does not get to own the id never releases a request that has already been paid
- passed A rejection does not get to own the id does not let one workspace re-drive verification of its own rejection
- passed The id is unique per provider, not on its own does not mistake an identical id from another provider for a duplicate
- passed Nor does a pending row nobody can ask about gives the id back when the workspace holding it has no key to answer with
- passed Nor does a pending row nobody can ask about leaves a pending report alone when its workspace can still be asked about it
- passed What is not being paid for, and why counts a report still waiting on the provider as waiting, not as nothing
- passed What is not being paid for, and why stops calling a report outstanding once the provider has answered for it
- passed What is not being paid for, and why gives the provider’s reason for every refusal, commonest first
- passed What is not being paid for, and why counts a refusal that has no reason on file rather than inventing one
- passed What is not being paid for, and why answers about the workspaces it was asked about and no others
- passed What is not being paid for, and why says nothing is outstanding for an account with no workspaces at all
- passed Taking an id back is one write leaves another workspace’s rows where they were when the insert fails
- passed Taking an id back is one write takes the id back for real when the insert succeeds
packages/core/src/__tests__/openrouter.test.ts in @offcut/core · 44 tests in the file
- passed Cost becomes integer millionths of a dollar, exactly converts a decimal price without a float rounding it
- passed Cost becomes integer millionths of a dollar, exactly is always an integer
- passed Cost becomes integer millionths of a dollar, exactly rounds half up below the smallest unit, and in one direction for everyone
- passed Cost becomes integer millionths of a dollar, exactly reads a price sent as a string as well as a number
- passed Cost becomes integer millionths of a dollar, exactly refuses anything that is not a usable amount
- passed Cost becomes integer millionths of a dollar, exactly refuses a number too large to be one generation
- passed Only an unknown generation is a rejection returns null when OpenRouter has never heard of the id
- passed Only an unknown generation is a rejection throws on a 500, so the report waits instead of being destroyed
- passed Only an unknown generation is a rejection throws on a rate limit
- passed Only an unknown generation is a rejection throws on a rejected key, so a key rotation does not delete earnings
- passed Only an unknown generation is a rejection throws when OpenRouter cannot be reached at all
- passed A body we cannot read defers rather than paying zero throws on no data object
- passed A body we cannot read defers rather than paying zero throws on a data object with no cost in it
- passed A body we cannot read defers rather than paying zero throws on a cost that is not a number
- passed A body we cannot read defers rather than paying zero throws on a null cost
- passed A body we cannot read defers rather than paying zero throws on a negative cost
- passed A body we cannot read defers rather than paying zero throws on data that is not an object
- passed A body we cannot read defers rather than paying zero throws when the response is not JSON at all
- passed A body we cannot read defers rather than paying zero still confirms a generation that genuinely cost nothing
- passed The answer is read from the fields OpenRouter actually sends reads total_cost, and the provider token counts
- passed The answer is read from the fields OpenRouter actually sends falls back to the normalised token counts when native ones are absent
- passed The answer is read from the fields OpenRouter actually sends records zero tokens rather than refusing, because tokens are not the money
- passed The answer is read from the fields OpenRouter actually sends reads usage only when total_cost is missing
- passed The answer is read from the fields OpenRouter actually sends asks about the right generation, with the right key
- passed The key never leaves the closure it was given to is not a property of the verifier
- passed The key never leaves the closure it was given to is not in the error thrown when the provider fails
- passed The key never leaves the closure it was given to is not in the error thrown when the host is unreachable
- passed Checking a key before it is stored accepts a key OpenRouter recognises
- passed Checking a key before it is stored separates a key OpenRouter refuses from an OpenRouter that is down
- passed Checking a key before it is stored separates a key that cannot be sent at all from an OpenRouter that is down
- passed Checking a key before it is stored says nothing about a key it could not send, not even which character was wrong
- passed Checking a key before it is stored leaves a report pending on a key it cannot send, and does not blame the network
- passed Checking a key before it is stored never returns anything about the key beyond whether it works
- passed Each workspace is verified against its own key confirms two workspaces that each paid with a different key
- passed Each workspace is verified against its own key would have rejected the second workspace under one shared verifier
- passed Each workspace is verified against its own key leaves a workspace with no linked key pending, never rejected
- passed Each workspace is verified against its own key hands back an id parked by a workspace that has no key to answer with
- passed Each workspace is verified against its own key does not let unverifiable rows starve the workspaces that can be verified
- passed Each workspace is verified against its own key defers a workspace whose credential cannot be decrypted
- passed Each workspace is verified against its own key decrypts a workspace key once however many rows it has
- passed Each workspace is verified against its own key settles a 404 as rejected and a 500 as still pending, through the whole path
- passed Linking a corrected key reopens what the wrong one refused confirms spend the previous key could not, once the right one is linked
- passed Linking a corrected key reopens what the wrong one refused does not reopen anything already confirmed, so nothing is paid twice
- passed Linking a corrected key reopens what the wrong one refused leaves the rejections of another workspace alone
apps/web/src/__tests__/link-usage-key.test.tsx in @offcut/web · 29 tests in the file
- passed What the screen says the key is for says it is used to read what a generation cost, before anyone pastes one
- passed What the screen says the key is for says no completions are made with it and it never spends the owner’s money
- passed What the screen says the key is for does not promise the spend comes back
- passed The key itself leaves the browser once, in a body, and never in the URL
- passed The key itself is not in the serialised DOM while it is sitting in the field
- passed The key itself is not in the serialised DOM after a failed link leaves it there to retry
- passed The key itself is gone from the page the moment it is stored
- passed The key itself is shown back as four characters and no more
- passed The key itself is never asked for by a request that could read it back
- passed A key that would not link says OpenRouter refused it, and says the key is the thing to fix
- passed A key that would not link says an unreachable provider is not a bad key, and not to revoke it
- passed A key that would not link does not tell somebody to wait when the key was never sent anywhere
- passed A key that would not link draws a key that could not be sent as the key being wrong, not as nobody knowing
- passed A key that would not link draws the two failures differently, because they mean different things
- passed A key that would not link explains a server that has no OFFCUT_SECRET_KEY instead of blaming the key
- passed A key that would not link says the key never left the page when the console cannot reach its own server
- passed A key that would not link prints the server’s own words even when it cannot classify them
- passed A key that would not link will not send an empty field as if it were a key
- passed A key that is linked says so, and offers to replace or unlink it rather than showing a form
- passed A key that is linked unlinks with a DELETE and reports the cleared status upwards
- passed A key that is linked says what unlinking does not take away
- passed A key that is linked says a workspace with no key can confirm nothing
- passed Reading which failure the server reported reads a rejection by OpenRouter as the key being wrong
- passed Reading which failure the server reported reads an unreachable OpenRouter as nobody having checked
- passed Reading which failure the server reported does not read a key that could not be sent as an OpenRouter outage
- passed Reading which failure the server reported reads a missing OFFCUT_SECRET_KEY as the server, not the key
- passed Reading which failure the server reported does not mistake this product’s own server being down for an OpenRouter outage
- passed Reading which failure the server reported reads a refusal to let this caller near the credential as exactly that
- passed Reading which failure the server reported classifies nothing it does not recognise
packages/acceptance/src/usage-tool-description.test.ts in @offcut/acceptance · 3 tests in the file
- passed The instruction text offcut_usage_report gives an agent exists at all, because the rest of this file is about what it says
- passed The instruction text offcut_usage_report gives an agent never tells an agent that extra spend adds nothing to what is distributed
- passed The instruction text offcut_usage_report gives an agent still tells an agent not to spend more in order to report more, and gives the true reason
packages/contracts/test/OffcutRewards.test.ts in @offcut/contracts · 27 tests in the file
- passed Claiming pays what was earned, once pays the full amount on a first claim
- passed Claiming pays what was earned, once pays only the difference when a later root raises the total
- passed Claiming pays what was earned, once refuses a second claim against the same root
- passed Claiming pays what was earned, once lets a recipient skip roots and claim once at the end
- passed Claiming pays what was earned, once lets anyone pay the gas, but pays only the named account
- passed A proof that was not published cannot be spent rejects an amount larger than the one in the tree
- passed A proof that was not published cannot be spent rejects one account's proof used for another
- passed A proof that was not published cannot be spent rejects a proof from a tree that was never published
- passed A proof that was not published cannot be spent cannot be tricked by passing an internal node as a leaf
- passed A proof that was not published cannot be spent pays nothing before the first root exists
- passed A proof that was not published cannot be spent refuses to pay more than it holds rather than paying partially
- passed A token that takes a cut of transfers reverts rather than paying 95% and recording 100%
- passed A token that takes a cut of transfers leaves nothing recorded, so the claim works once the fee is fixed
- passed The publisher key can misdirect rewards and cannot take them is the only key that can publish
- passed The publisher key can misdirect rewards and cannot take them cannot withdraw, upgrade, pause or replace itself
- passed The publisher key can misdirect rewards and cannot take them can be replaced by the owner, after which the old key is inert
- passed The publisher key can misdirect rewards and cannot take them cannot publish an empty root, which would strand every claim
- passed The publisher key can misdirect rewards and cannot take them can lower an amount, and an over-paid account is simply owed nothing more
- passed The owner's powers are what the page says they are can withdraw everything, which is the risk that is disclosed
- passed The owner's powers are what the page says they are cannot withdraw more than the contract holds
- passed The owner's powers are what the page says they are names the token once and refuses to change it afterwards
- passed The owner's powers are what the page says they are can be deployed before the token exists, and pays once it is named
- passed The owner's powers are what the page says they are pausing stops publishing and claiming, and never blocks withdrawal
- passed The owner's powers are what the page says they are transfers ownership in two steps, so a typo cannot orphan the contract
- passed The owner's powers are what the page says they are is the only one who can upgrade, and state survives the upgrade
- passed The owner's powers are what the page says they are refuses a zero address wherever one would break it
- passed The owner's powers are what the page says they are locks the implementation so nobody can initialise it directly
packages/rewards/src/__tests__/merkle.test.ts in @offcut/rewards · 17 tests in the file
- passed Agreement with the contract suite produces the same root as the contract builder for 1 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 2 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 3 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 4 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 5 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 7 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 16 entries
- passed Agreement with the contract suite produces the same root as the contract builder for 17 entries
- passed Agreement with the contract suite produces the same proofs, not merely the same root
- passed Agreement with the contract suite hashes a leaf the same way
- passed A root that identifies a set of balances does not depend on the order the rows arrived in
- passed A root that identifies a set of balances changes when a single base unit changes
- passed Entries the tree will not take refuses two leaves for one address
- passed Entries the tree will not take refuses an entry owed nothing
- passed Entries the tree will not take refuses a negative balance
- passed Entries the tree will not take refuses something that is not an address
- passed Entries the tree will not take refuses to publish an empty tree
packages/rewards/src/__tests__/ledger.test.ts in @offcut/rewards · 14 tests in the file
- passed Running a period again writes nothing the second time
- passed Running a period again does not double the totals
- passed Running a period again refuses a period already settled at a different figure
- passed Running a period again refuses to add an address to a period that is already settled
- passed Periods that would pay for the same credits twice refuses a window that overlaps one on file
- passed Periods that would pay for the same credits twice accepts the period that starts where the last one ended
- passed Cumulative totals only ever increase
- passed Cumulative totals refuse a negative accrual, which would take back money already claimable
- passed Cumulative totals sum every address, sorted so two runs produce one order
- passed Cumulative totals leave out a period that has not closed yet
- passed Cumulative totals survive the deletion of the workspace that earned them
- passed Rows the ledger will not hold refuses an address that is not canonical, rather than paying a guess
- passed Rows the ledger will not hold refuses two rows for one address in one period
- passed Rows the ledger will not hold writes no row for an address owed nothing
apps/publisher/src/__tests__/publishing.test.ts in @offcut/publisher · 15 tests in the file
- passed Deciding whether to send a transaction sends nothing when nobody earned anything in the window
- passed Deciding whether to send a transaction sends nothing for a quiet period, because the root has not moved
- passed Deciding whether to send a transaction sends one transaction when somebody does earn
- passed Deciding whether to send a transaction does not publish into a paused contract
- passed When a transaction does not land waits for the next period instead of retrying immediately
- passed When a transaction does not land heals on the next period, because the root is cumulative
- passed When a transaction does not land keeps settling periods even while publishing keeps failing
- passed The proof files writes a proof that verifies against the root that was published
- passed The proof files files a root under the index the chain actually gave it
- passed The proof files rewrites the files for a root that reached the chain but left no proofs behind
- passed The proof files does not rewrite files that are already there
- passed The proof files carries the cumulative total, not the period amount
- passed A dry run does everything except send the transaction
- passed A dry run marks what it writes, so a rehearsal never reads as a real publication
- passed A dry run still skips a root it has already decided not to send
apps/publisher/src/__tests__/settlement.test.ts in @offcut/publisher · 14 tests in the file
- passed Settling a period settles the period that just closed, and only that one, on a first start
- passed Settling a period settles a period once however many times the service restarts inside it
- passed Settling a period does not pay twice when the cursor never reached the disk
- passed Settling a period says in the log that a period was already in the ledger
- passed Coming back from downtime settles every period missed while it was down, oldest first, none skipped
- passed Coming back from downtime pays one transaction for a catch-up, not one per period
- passed Coming back from downtime leaves no gap between the period before the outage and the one after it
- passed Coming back from downtime carries on from the cursor rather than from now, after a long outage
- passed The cursor itself records the last settled period, never one ahead of the ledger
- passed The cursor itself is written on a first start before any period is settled
- passed The cursor itself does not advance past a period the ledger refused
- passed A cursor that cannot be trusted refuses to start rather than treating an unreadable cursor as a fresh one
- passed A cursor that cannot be trusted survives a round trip through the disk
- passed Periods and the clock settles the window the credits were actually written in
apps/web/src/__tests__/claim-rewards.test.tsx in @offcut/web · 21 tests in the file
- passed Before there is anything to claim from says the points are real and claiming is not live
- passed Before there is anything to claim from explains that nothing earned expires while it waits
- passed Before there is anything to claim from asks for nothing from the server before a contract exists
- passed Before there is anything to claim from shouts about a configured address that is not an address
- passed Before there is anything to claim from points at the payout field when no address is set
- passed When a root has not reached this address reports that nothing has been published, as a state rather than a fault
- passed When a root has not reached this address says an address absent from the current root will appear in the next one
- passed When a root has not reached this address shows the server’s own words when the file could not be read
- passed A proof built for another deployment is refused here rather than reverted at the wallet
- passed A proof built for another deployment is refused when it was built for another chain
- passed A proof built for another deployment names both sides, so it is clear which one is wrong
- passed The claimable figure is withheld until the chain says what was already paid
- passed The claimable figure is earnings minus what the chain already paid
- passed The claimable figure prints the exact base units beside it
- passed The claimable figure says so plainly when everything in the root has been taken
- passed Sending the claim claims for the address in the proof, with the amount in the proof
- passed Sending the claim sends to the linked address even when another wallet is connected
- passed Sending the claim offers to switch the network instead of sending on the wrong one
- passed Sending the claim asks for a wallet before offering to send anything
- passed Sending the claim shows the wallet’s own error rather than a rewritten one
- passed When the distributor has no token yet says a claim would be refused on chain, and does not offer one
apps/web/src/__tests__/rewards.test.ts in @offcut/web · 31 tests in the file
- passed A published file is refused unless every field can be vouched for accepts the shape the publisher writes
- passed A published file is refused unless every field can be vouched for stores addresses in one case, so a lookup is a plain comparison
- passed A published file is refused unless every field can be vouched for refuses an amount written as a number
- passed A published file is refused unless every field can be vouched for refuses an amount in exponent form, which is what a number turns into
- passed A published file is refused unless every field can be vouched for refuses a fractional or signed amount
- passed A published file is refused unless every field can be vouched for refuses a root that is not 32 bytes
- passed A published file is refused unless every field can be vouched for refuses a proof containing anything that is not a hash
- passed A published file is refused unless every field can be vouched for refuses an entry with no usable account
- passed A published file is refused unless every field can be vouched for refuses a root with no entries, which no tree builder can produce
- passed A published file is refused unless every field can be vouched for refuses anything that is not an object at all
- passed A published file is refused unless every field can be vouched for names what was wrong, so a bad publish can be fixed rather than guessed at
- passed Finding an address in a root matches however the address was written
- passed Finding an address in a root returns null rather than a neighbouring entry when the address is absent
- passed What is left to claim is earnings minus what the chain has already paid
- passed What is left to claim is zero, never negative, when a republished root lowers what is owed
- passed What is left to claim is exact past the point where a JavaScript number stops being one
- passed Displayed amounts are the amounts that would move neither rounds nor truncates the fraction
- passed Displayed amounts are the amounts that would move shows a whole token as a whole number
- passed Displayed amounts are the amounts that would move does not lose a dust amount by rounding it to zero
- passed Displayed amounts are the amounts that would move honours a different decimals setting rather than assuming eighteen
- passed A proof is only offered against the deployment it was built for accepts a file built for this contract on this chain
- passed A proof is only offered against the deployment it was built for accepts it however the configured address was capitalised
- passed A proof is only offered against the deployment it was built for refuses a file built for another contract
- passed A proof is only offered against the deployment it was built for refuses a file built for another chain
- passed Whether there is a contract to claim from treats an unset address as not deployed
- passed Whether there is a contract to claim from treats a configured non-address as a fault, not as pre-launch
- passed Whether there is a contract to claim from accepts a real address
- passed How many base units make a token defaults to eighteen when nothing is configured
- passed How many base units make a token does not read an unset build argument as zero decimals
- passed How many base units make a token ignores anything that is not a whole, plausible number of decimals
- passed How many base units make a token honours a real setting, because not every token is eighteen
What is open, and who it waits on
apps/web/public/proof/build-log.json
Not under version control
There is no git repository here, which is why the commit field at the top of this page is empty, and there is not going to be one: the owner has decided against it. What stands in for a history is a dated archive of the whole tree, taken before and after every large change (pnpm snapshot, kept outside the repository). A stranger still cannot read the work commit by commit.
team owes itNothing is hosted
The site, the API and the console run on a laptop. A machine to host them on has been supplied, nothing has been put on it, and the domain has not been chosen — so there is still no link to hand anyone.
owner decidesThe $OFFCUT contract address
apps/web/src/lib/token.ts holds one field for it and says the token is not published while it is empty. The owner supplies the address; no other code changes.
owner decidesNo real wallet has clicked through
Wallet sign-in and the reward claim are covered by tests against stubbed transports. Neither has been driven end to end by a real wallet on the testnet.
owner decidesThe reward contracts are unaudited and deployed nowhere
packages/contracts is tested locally and nowhere else: the suite in the run above is all of it, and it runs against hardhat’s in-process chain rather than a real one. It did not run at all until 2026-09-18, when the chai it imports was finally installed. The contract is upgradeable and the owner can withdraw, which the site says plainly. No audit has been commissioned.
team owes itThe packages are not published
The npm scope is reserved and two pre-publish reviews are clean, but publishing is a team decision that has not been taken.
owner decidesEverything on screen comes from the demo workspace
The console screenshots show the seeded demo store (packages/core/src/seed.ts), because there is no production use yet. The two throwaway workspaces the owner typed while testing were deleted on 2026-09-19, so the shelf holds the seeded workspace and nothing else. One more scrap is still in the store under the account used to test wallet sign-in; it belongs to that account rather than to the demo one, so it appears on no screenshot, and it is left alone.
team owes itSection 8 of the specification, OPEN-3, is answered except for one number
Record text, import batches, the retrieval context and, since 2026-09-19, the number of live agents in a workspace are all capped in packages/core/src/types.ts, enforced in packages/core/src/admin.ts and pinned by packages/core/src/__tests__/agent-limit.test.ts. The maximum size of a workspace itself — the fifth thing the clause names — is still set nowhere and pinned by no test.
team owes itSection 8 of the specification, OPEN-1, is the owner's call
The MCP server and a config generator exist (scripts/mcp-config.mjs), but which clients are supported has not been settled; one candidate was already dropped.
owner decidesNo recording of the integration
There is no short video of an MCP client connecting and two agents merging memory. Reading this page is not the same as watching it work.
team owes itRun it, and compare
apps/web/public/proof · served from this site
If your run disagrees with this page, the page is wrong — the figures are a record of one run, and their age is printed with them. pnpm proof:check answers whether that run is still about the current tree, without running anything.
Installs the workspace. Node is the only thing needed first.
pnpm installRuns every package’s suite and rewrites the numbers on this page. It is the same command that wrote them.
pnpm proofRuns this site’s own suite on its own.
pnpm --filter @offcut/web test