chore: sync the frontend template from 4dc033c to 8157ad4

The product was materialized from the template at `4dc033c` and has stayed
on it through 43 template commits, so it was missing all three rounds of
adapter remediation — including files it never had, such as the shared
`abortable-operation` primitive and the `exact-snapshot` decoder that
later fixes are written against. Taking only the newest round was not
possible for that reason: the delta is coherent only as a whole.

The product had not touched `src/adapters` at all since materialization,
so the 140-file delta applied with a three-way merge and no conflicts.
`package.json` was the single overlap and merged cleanly: the product owns
`name`, the template contributed `check:adapter-inventory`,
`check:remediation-ledger` and the image-resolve-signal type fixture.
All 24 product-owned files — README, index.html, CI workflow, i18n
catalog, home page, generated schemas, evidence scripts, component and
visual snapshots — are byte-identical to `main`.

`template.lock.json` now pins the synced revision and tree.

Verified in this repository, not inherited from the template: six type
projects, lint, nine gates (adapter inventory, remediation ledger,
registries, diagnostics, realtime boundaries, architecture, browser
file/storage boundaries, optional recipes, documentation), the production
build, and 2,054 of 2,073 tests. The 19 failures are all in
`tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing
sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template
records; four suites that failed once under parallel load pass in
isolation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-15 12:04:58 +09:00
co-authored by Claude Opus 5
parent 002ba3624e
commit 4bff9ca151
142 changed files with 23010 additions and 1544 deletions
+300 -3
View File
@@ -290,7 +290,10 @@ function createWorker(
return browserDataSuccess(undefined);
},
async cleanupTransaction() {
return browserDataSuccess(undefined);
return browserDataSuccess({ kind: "CLEANED" as const });
},
async abortPreparedPut() {
return browserDataSuccess({ kind: "CLEANED" as const });
},
async finalizePut() {
return browserDataSuccess(undefined);
@@ -349,6 +352,123 @@ describe("OPFS byte-store coordinator", () => {
expect(JSON.stringify(observations)).not.toContain("object_12345678");
});
/**
* NS-04. Awaiting `journal.complete` without checking it reported a settled
* transaction that nobody settled: the caller saw plain success and success
* telemetry while the durable row stayed `COMMITTED`, so the reconcile
* backlog grew invisibly.
*/
it("does not report a settled write when the journal cannot complete it", async () => {
const journal = createJournal();
journal.complete = async () =>
browserDataFailure("UNAVAILABLE", "INDEXEDDB_WRITE", {
retryable: true,
recovery: "RECONCILE",
});
const observations: { operation: string; outcome: string }[] = [];
const adapter = createOpfsByteStoreAdapter({
journal,
worker: createWorker(),
scope,
storagePolicy,
policy: {
...resolveOpfsRuntimePolicy(),
chunkSizeBytes: 64 * 1024,
maxObjectBytes: 64 * 1024,
maxChunkCount: 1,
},
createTransactionId: () => "transaction_12345678",
now: () => 100,
observer: (event) =>
observations.push(event as { operation: string; outcome: string }),
});
const result = await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1, 2, 3])),
});
expect(result.ok).toBe(false);
if (!result.ok) {
// The payload is durable, so recovery is reconciliation, not a rewrite.
expect(result.error.recovery).toBe("RECONCILE");
expect(result.error.operation).toBe("OBJECT_WRITE");
}
expect(
observations.some(
(event) =>
event.operation === "OBJECT_WRITE" && event.outcome === "SUCCEEDED",
),
).toBe(false);
// The committed row stays the reconciler's authority.
expect(journal.transactions.get("transaction_12345678")?.phase).toBe(
"COMMITTED",
);
expect(journal.objects.get("object_12345678")).toBeDefined();
});
it("records maintenance debt when a committed delete cannot be completed", async () => {
const journal = createJournal();
const observations: { operation: string; outcome: string }[] = [];
const policy = {
...resolveOpfsRuntimePolicy(),
chunkSizeBytes: 64 * 1024,
maxObjectBytes: 64 * 1024,
maxChunkCount: 1,
};
const writer = createOpfsByteStoreAdapter({
journal,
worker: createWorker(),
scope,
storagePolicy,
policy,
createTransactionId: () => "transaction_12345678",
now: () => 100,
});
await writer.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1, 2, 3])),
});
// The journal can commit the deletion but cannot settle the transaction.
const remover = createOpfsByteStoreAdapter({
journal: {
...journal,
complete: async () =>
browserDataFailure("UNAVAILABLE", "INDEXEDDB_WRITE", {
retryable: true,
recovery: "RECONCILE",
}),
},
worker: createWorker(),
scope,
storagePolicy,
policy,
createTransactionId: () => "transaction_87654321",
now: () => 200,
observer: (event) =>
observations.push(event as { operation: string; outcome: string }),
});
// Logical deletion is already committed, so the caller must not be asked to
// repeat a non-idempotent delete — but it is not a settled success either.
const result = await remover.objects.remove({
objectId: "object_12345678",
expectedGeneration: 1,
});
expect(result.ok).toBe(true);
const deleteOutcomes = observations
.filter((event) => event.operation === "OBJECT_DELETE")
.map((event) => event.outcome);
expect(deleteOutcomes.at(-1)).toBe("DEGRADED");
expect(deleteOutcomes).not.toContain("SUCCEEDED");
});
it("deep-snapshots scope and policy at composition against caller mutation", async () => {
const journal = createJournal();
const mutableScope = { ...scope };
@@ -501,7 +621,183 @@ describe("OPFS byte-store coordinator", () => {
expect(replacementWorkerMethod).not.toHaveBeenCalled();
});
it("keeps a committed journal row for reconciliation when cleanup fails", async () => {
it("keeps PREPARING journal when compensating cleanup is aborted or unavailable", async () => {
for (const failing of [
{
async abortPreparedPut() {
return browserDataFailure("ABORTED", "OBJECT_RECONCILE");
},
},
{
async abortPreparedPut() {
return browserDataSuccess({ kind: "EFFECT_UNKNOWN" as const });
},
},
]) {
const journal = createJournal();
const worker = createWorker({
async preparePut() {
return browserDataFailure("QUOTA_EXCEEDED", "OBJECT_WRITE");
},
...failing,
});
const adapter = createOpfsByteStoreAdapter({
journal,
worker,
scope,
storagePolicy,
createTransactionId: () => "transaction_12345678",
now: () => 100,
});
const result = await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1])),
});
expect(result.ok).toBe(false);
// The journal row is the only durable evidence that staging bytes may
// still exist, so it survives an unconfirmed compensation.
expect(
journal.transactions.get("transaction_12345678")?.phase,
).toBe("PREPARING");
}
});
it("does not roll back journal after an unknown worker mutation effect", async () => {
const journal = createJournal();
const worker = createWorker({
async abortPreparedPut() {
return browserDataSuccess({ kind: "EFFECT_UNKNOWN" as const });
},
});
const rollback = vi.spyOn(journal, "rollback");
vi.spyOn(journal, "markFilesReady").mockResolvedValueOnce(
browserDataFailure("UNAVAILABLE", "OBJECT_WRITE"),
);
const adapter = createOpfsByteStoreAdapter({
journal,
worker,
scope,
storagePolicy,
createTransactionId: () => "transaction_12345678",
now: () => 100,
});
const result = await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1])),
});
expect(result.ok).toBe(false);
expect(rollback).not.toHaveBeenCalled();
});
it("holds the OPFS mutation lease until exact physical cleanup completes", async () => {
const journal = createJournal();
const observed: Array<Readonly<Record<string, unknown>>> = [];
const worker = createWorker({
async preparePut() {
return browserDataFailure("QUOTA_EXCEEDED", "OBJECT_WRITE");
},
async abortPreparedPut(request) {
observed.push({ ...request });
return browserDataSuccess({ kind: "CLEANED" as const });
},
});
const compensation = new AbortController();
const caller = new AbortController();
caller.abort();
const adapter = createOpfsByteStoreAdapter({
journal,
worker,
scope,
storagePolicy,
createTransactionId: () => "transaction_12345678",
createPhysicalGenerationId: () => "f".repeat(32) as never,
compensationSignal: compensation.signal,
now: () => 100,
});
await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1])),
});
expect(observed).toHaveLength(1);
expect(observed[0]).toMatchObject({
transactionId: "transaction_12345678",
physicalGenerationId: "f".repeat(32),
});
// Compensation never inherits the caller signal.
expect(observed[0]?.signal).toBe(compensation.signal);
expect(
journal.transactions.has("transaction_12345678"),
).toBe(false);
});
it("delayed stale cleanup cannot delete a reused logical generation", async () => {
const journal = createJournal();
const issued: string[] = [];
let nextToken = 0;
const worker = createWorker({
async abortPreparedPut(request) {
issued.push(request.physicalGenerationId);
return browserDataSuccess({ kind: "CLEANED" as const });
},
});
let transaction = 0;
// T1 abandons its prepared put at the same logical generation 1.
vi.spyOn(journal, "markFilesReady").mockResolvedValueOnce(
browserDataFailure("UNAVAILABLE", "OBJECT_WRITE"),
);
const adapter = createOpfsByteStoreAdapter({
journal,
worker,
scope,
storagePolicy,
createTransactionId: () => `transaction_1234567${(transaction += 1)}`,
createPhysicalGenerationId: () =>
String(nextToken += 1).padStart(32, "0") as never,
now: () => 100,
});
const first = await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1])),
});
expect(first.ok).toBe(false);
// T2 legitimately reuses logical generation 1 with a different token.
const second = await adapter.objects.put({
objectId: "object_12345678",
expectedGeneration: null,
mediaType: "application/octet-stream",
source: sourceFrom(new Uint8Array([1])),
});
expect(second.ok).toBe(true);
if (second.ok) expect(second.value.generation).toBe(1);
// The stale compensation targeted only T1's physical token.
expect(issued).toEqual([String(1).padStart(32, "0")]);
const stored = journal.objects.get("object_12345678");
expect(stored?.physicalSchemaVersion).toBe(1);
});
/**
* STO-RR-01. Finalization runs after the commit fence, so the payload is
* durable and the journal row must survive for reconciliation. What the
* caller must not be told is that the write settled: the previous generation
* and the staging directory are still there.
*/
it("reports a failed finalization instead of a plain success", async () => {
const journal = createJournal();
const worker = createWorker({
async finalizePut() {
@@ -533,7 +829,8 @@ describe("OPFS byte-store coordinator", () => {
source: sourceFrom(new Uint8Array([1])),
});
expect(result.ok).toBe(true);
expect(result.ok).toBe(false);
expect(result.ok ? null : result.error.operation).toBe("OBJECT_WRITE");
expect(
journal.transactions.get("transaction_12345678")?.phase,
).toBe("COMMITTED");