arthur/clips/templates/clips/index.html

32 lines
1.3 KiB
HTML
Raw Normal View History

Serve the document from a Django backend, split into three tiers Step 9. The tier split was the work; Django was the easy half. Tier 1 — the authored scene — is the document, and it is addressed as independently versioned leaves rather than saved whole, so one vertex drag cannot clobber a collaborator's keying. `domain/leaf` is the document as path -> value; `domain/wire` puts it on the wire as transit, because JSON has neither integer map keys nor keywords and a save would quietly turn `{0 v}` into `{"0" v}`. Tier 2 — the dense channel blocks — is content-addressed by a hash over every input, with the detector version inside every key through the analysis the block descriptor names. `flow/address`'s `block-knobs` is the invalidation table, and `address-test` does not trust it: it re-freezes the take once per knob and asserts the biconditional, that a block's bytes changed if and only if its key changed. That found `brow-pos` not depending on `contour-avg` — the brow ring is smoothed, the raise is not. Tier 3 — frames and audio — is served by the hash of its bytes out of the same store. A manifest now names frames and carries a URL for each, so the frame layout stopped being a shared secret between a shell script and a ClojureScript namespace, and the `?v=` cache-buster went with it: a blob's name is the hash of its contents, so a stale copy is not a thing that can happen. The synthetic take's `audio.wav` moved to `static/arthur/` — an asset the project owns, not an extraction that churns. The server verifies rather than trusting a name it was handed: it recomputes every key from the descriptor stored beside it, refuses an analysis that declares no detector version, and refuses a document naming blocks it does not hold. It hashes the descriptor TEXT, because JS prints an integral double as `1` and Python as `1.0`, and a scheme where both ends re-render the numbers disagrees on the first parameter that happens to be whole. Two loose ends from step 8 closed on the way. `pack` no longer takes a `(track, frame)` predicate whose call sites each re-derived a feature from an index — every track names the feature it follows, which deleted five hand-maintained mappings. And `:dev-http` is gone: Django serves the page, shadow-cljs only builds into the staticfiles tree. 227 CLJS tests, 31 Django tests, green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 01:11:41 -04:00
{% load static %}<!doctype html>
{% comment %}
The host page, served by Django since port-plan step 9.
It carries no styles of its own any more. They are `static/arthur/app.css`, which
staticfiles serves from the same tree as the bundle — the page grew a five-pane
application chrome and "the styles" stopped being a thing you read in passing on
the way to the markup.
The bundle is unchanged: shadow-cljs writes it into `static/arthur/js` and
staticfiles serves it from there, so `manage.py runserver` and `shadow-cljs watch
app` are the whole dev loop with nothing copying files between them.
Serve the document from a Django backend, split into three tiers Step 9. The tier split was the work; Django was the easy half. Tier 1 — the authored scene — is the document, and it is addressed as independently versioned leaves rather than saved whole, so one vertex drag cannot clobber a collaborator's keying. `domain/leaf` is the document as path -> value; `domain/wire` puts it on the wire as transit, because JSON has neither integer map keys nor keywords and a save would quietly turn `{0 v}` into `{"0" v}`. Tier 2 — the dense channel blocks — is content-addressed by a hash over every input, with the detector version inside every key through the analysis the block descriptor names. `flow/address`'s `block-knobs` is the invalidation table, and `address-test` does not trust it: it re-freezes the take once per knob and asserts the biconditional, that a block's bytes changed if and only if its key changed. That found `brow-pos` not depending on `contour-avg` — the brow ring is smoothed, the raise is not. Tier 3 — frames and audio — is served by the hash of its bytes out of the same store. A manifest now names frames and carries a URL for each, so the frame layout stopped being a shared secret between a shell script and a ClojureScript namespace, and the `?v=` cache-buster went with it: a blob's name is the hash of its contents, so a stale copy is not a thing that can happen. The synthetic take's `audio.wav` moved to `static/arthur/` — an asset the project owns, not an extraction that churns. The server verifies rather than trusting a name it was handed: it recomputes every key from the descriptor stored beside it, refuses an analysis that declares no detector version, and refuses a document naming blocks it does not hold. It hashes the descriptor TEXT, because JS prints an integral double as `1` and Python as `1.0`, and a scheme where both ends re-render the numbers disagrees on the first parameter that happens to be whole. Two loose ends from step 8 closed on the way. `pack` no longer takes a `(track, frame)` predicate whose call sites each re-derived a feature from an index — every track names the feature it follows, which deleted five hand-maintained mappings. And `:dev-http` is gone: Django serves the page, shadow-cljs only builds into the staticfiles tree. 227 CLJS tests, 31 Django tests, green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 01:11:41 -04:00
The CSRF token is rendered so that Django sets its cookie, which is what
`arthur.fx.http` reads to write the `X-CSRFToken` header. Saves are ordinary POSTs
and PUTs with ordinary CSRF protection — no endpoint in this app is exempt.
{% endcomment %}
Port steps 0-1: scaffold, the oracle, and the pure bottom Scaffolds frontend/ (shadow-cljs, reagent 1.2.0, re-frame 1.4.3) and ports everything below the data model, with the JS kept as a numeric oracle. domain/landmarks index tables, verbatim domain/ring subsample, offset, simplicity domain/geom similarity fit, procrustes, moving average domain/raster indexed scanline fill, stencil, disc, rect domain/palette the ramp, and the no-sampled-RGB rule 58 tests, 166 assertions. Parity with js/ on the identical 72-frame synthetic track: fit-similarity, procrustes-mean, fit-residual, moving-average, smooth-transforms, offset-ring and subsample-slots to 1e-9; the raster pixel-for-pixel over the whole buffer. Three deviations from the JS, each for a reason: - synth.cljs jitters from a SEEDED generator, not Math.random. Parity is only checkable if both sides can be handed the same track, and a failing assertion has to be reproducible. `:rand-fn` takes the generator over, so oracle.mjs stubs js/Math.random and js/ itself stays untouched. - raster/->rgba replaces toImageData. ImageData is a DOM type and domain/ may not touch the DOM; returning plain bytes also lets the no-intermediate-colours assertion run in node. ui/canvas wraps it later. - offset-ring lives in domain/ring, not domain/geom, per architecture.md: it is an operation on an ordered traversal, not on a transform. Step 1's "done" also names the swapped-iris vote, but pairIrises is in pipeline.js and belongs to step 7. The precondition is asserted instead -- `:swap-iris` really does move both blocks -- so the vote will have a track that disagrees with it when it arrives. One finding, recorded in full in the test that measures it: smooth-transforms buys nothing on the synthetic track. Against jitter-free ground truth, radius 1 helps by 17% on one noise realisation and hurts by 0.5% on another, so its benefit is within noise; from radius 2 up the cost is unambiguous, and by radius 5 the filter is below the true motion's own high-frequency energy, i.e. smoothing away performance. The test pins the shape of the knob rather than a preferred value. This may say more about the synth's jitter being unrealistically small (+/-0.001 normalised) than about the knob; step 6 settles it on real footage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 14:43:34 -04:00
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>arthur</title>
<link rel="stylesheet" href="{% static 'arthur/app.css' %}?v={{ css_version }}">
Port steps 0-1: scaffold, the oracle, and the pure bottom Scaffolds frontend/ (shadow-cljs, reagent 1.2.0, re-frame 1.4.3) and ports everything below the data model, with the JS kept as a numeric oracle. domain/landmarks index tables, verbatim domain/ring subsample, offset, simplicity domain/geom similarity fit, procrustes, moving average domain/raster indexed scanline fill, stencil, disc, rect domain/palette the ramp, and the no-sampled-RGB rule 58 tests, 166 assertions. Parity with js/ on the identical 72-frame synthetic track: fit-similarity, procrustes-mean, fit-residual, moving-average, smooth-transforms, offset-ring and subsample-slots to 1e-9; the raster pixel-for-pixel over the whole buffer. Three deviations from the JS, each for a reason: - synth.cljs jitters from a SEEDED generator, not Math.random. Parity is only checkable if both sides can be handed the same track, and a failing assertion has to be reproducible. `:rand-fn` takes the generator over, so oracle.mjs stubs js/Math.random and js/ itself stays untouched. - raster/->rgba replaces toImageData. ImageData is a DOM type and domain/ may not touch the DOM; returning plain bytes also lets the no-intermediate-colours assertion run in node. ui/canvas wraps it later. - offset-ring lives in domain/ring, not domain/geom, per architecture.md: it is an operation on an ordered traversal, not on a transform. Step 1's "done" also names the swapped-iris vote, but pairIrises is in pipeline.js and belongs to step 7. The precondition is asserted instead -- `:swap-iris` really does move both blocks -- so the vote will have a track that disagrees with it when it arrives. One finding, recorded in full in the test that measures it: smooth-transforms buys nothing on the synthetic track. Against jitter-free ground truth, radius 1 helps by 17% on one noise realisation and hurts by 0.5% on another, so its benefit is within noise; from radius 2 up the cost is unambiguous, and by radius 5 the filter is below the true motion's own high-frequency energy, i.e. smoothing away performance. The test pins the shape of the knob rather than a preferred value. This may say more about the synth's jitter being unrealistically small (+/-0.001 normalised) than about the knob; step 6 settles it on real footage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 14:43:34 -04:00
</head>
<body>
Serve the document from a Django backend, split into three tiers Step 9. The tier split was the work; Django was the easy half. Tier 1 — the authored scene — is the document, and it is addressed as independently versioned leaves rather than saved whole, so one vertex drag cannot clobber a collaborator's keying. `domain/leaf` is the document as path -> value; `domain/wire` puts it on the wire as transit, because JSON has neither integer map keys nor keywords and a save would quietly turn `{0 v}` into `{"0" v}`. Tier 2 — the dense channel blocks — is content-addressed by a hash over every input, with the detector version inside every key through the analysis the block descriptor names. `flow/address`'s `block-knobs` is the invalidation table, and `address-test` does not trust it: it re-freezes the take once per knob and asserts the biconditional, that a block's bytes changed if and only if its key changed. That found `brow-pos` not depending on `contour-avg` — the brow ring is smoothed, the raise is not. Tier 3 — frames and audio — is served by the hash of its bytes out of the same store. A manifest now names frames and carries a URL for each, so the frame layout stopped being a shared secret between a shell script and a ClojureScript namespace, and the `?v=` cache-buster went with it: a blob's name is the hash of its contents, so a stale copy is not a thing that can happen. The synthetic take's `audio.wav` moved to `static/arthur/` — an asset the project owns, not an extraction that churns. The server verifies rather than trusting a name it was handed: it recomputes every key from the descriptor stored beside it, refuses an analysis that declares no detector version, and refuses a document naming blocks it does not hold. It hashes the descriptor TEXT, because JS prints an integral double as `1` and Python as `1.0`, and a scheme where both ends re-render the numbers disagrees on the first parameter that happens to be whole. Two loose ends from step 8 closed on the way. `pack` no longer takes a `(track, frame)` predicate whose call sites each re-derived a feature from an index — every track names the feature it follows, which deleted five hand-maintained mappings. And `:dev-http` is gone: Django serves the page, shadow-cljs only builds into the staticfiles tree. 227 CLJS tests, 31 Django tests, green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 01:11:41 -04:00
{% csrf_token %}
Port steps 0-1: scaffold, the oracle, and the pure bottom Scaffolds frontend/ (shadow-cljs, reagent 1.2.0, re-frame 1.4.3) and ports everything below the data model, with the JS kept as a numeric oracle. domain/landmarks index tables, verbatim domain/ring subsample, offset, simplicity domain/geom similarity fit, procrustes, moving average domain/raster indexed scanline fill, stencil, disc, rect domain/palette the ramp, and the no-sampled-RGB rule 58 tests, 166 assertions. Parity with js/ on the identical 72-frame synthetic track: fit-similarity, procrustes-mean, fit-residual, moving-average, smooth-transforms, offset-ring and subsample-slots to 1e-9; the raster pixel-for-pixel over the whole buffer. Three deviations from the JS, each for a reason: - synth.cljs jitters from a SEEDED generator, not Math.random. Parity is only checkable if both sides can be handed the same track, and a failing assertion has to be reproducible. `:rand-fn` takes the generator over, so oracle.mjs stubs js/Math.random and js/ itself stays untouched. - raster/->rgba replaces toImageData. ImageData is a DOM type and domain/ may not touch the DOM; returning plain bytes also lets the no-intermediate-colours assertion run in node. ui/canvas wraps it later. - offset-ring lives in domain/ring, not domain/geom, per architecture.md: it is an operation on an ordered traversal, not on a transform. Step 1's "done" also names the swapped-iris vote, but pairIrises is in pipeline.js and belongs to step 7. The precondition is asserted instead -- `:swap-iris` really does move both blocks -- so the vote will have a track that disagrees with it when it arrives. One finding, recorded in full in the test that measures it: smooth-transforms buys nothing on the synthetic track. Against jitter-free ground truth, radius 1 helps by 17% on one noise realisation and hurts by 0.5% on another, so its benefit is within noise; from radius 2 up the cost is unambiguous, and by radius 5 the filter is below the true motion's own high-frequency energy, i.e. smoothing away performance. The test pins the shape of the knob rather than a preferred value. This may say more about the synth's jitter being unrealistically small (+/-0.001 normalised) than about the knob; step 6 settles it on real footage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 14:43:34 -04:00
<div id="app"></div>
Serve the document from a Django backend, split into three tiers Step 9. The tier split was the work; Django was the easy half. Tier 1 — the authored scene — is the document, and it is addressed as independently versioned leaves rather than saved whole, so one vertex drag cannot clobber a collaborator's keying. `domain/leaf` is the document as path -> value; `domain/wire` puts it on the wire as transit, because JSON has neither integer map keys nor keywords and a save would quietly turn `{0 v}` into `{"0" v}`. Tier 2 — the dense channel blocks — is content-addressed by a hash over every input, with the detector version inside every key through the analysis the block descriptor names. `flow/address`'s `block-knobs` is the invalidation table, and `address-test` does not trust it: it re-freezes the take once per knob and asserts the biconditional, that a block's bytes changed if and only if its key changed. That found `brow-pos` not depending on `contour-avg` — the brow ring is smoothed, the raise is not. Tier 3 — frames and audio — is served by the hash of its bytes out of the same store. A manifest now names frames and carries a URL for each, so the frame layout stopped being a shared secret between a shell script and a ClojureScript namespace, and the `?v=` cache-buster went with it: a blob's name is the hash of its contents, so a stale copy is not a thing that can happen. The synthetic take's `audio.wav` moved to `static/arthur/` — an asset the project owns, not an extraction that churns. The server verifies rather than trusting a name it was handed: it recomputes every key from the descriptor stored beside it, refuses an analysis that declares no detector version, and refuses a document naming blocks it does not hold. It hashes the descriptor TEXT, because JS prints an integral double as `1` and Python as `1.0`, and a scheme where both ends re-render the numbers disagrees on the first parameter that happens to be whole. Two loose ends from step 8 closed on the way. `pack` no longer takes a `(track, frame)` predicate whose call sites each re-derived a feature from an index — every track names the feature it follows, which deleted five hand-maintained mappings. And `:dev-http` is gone: Django serves the page, shadow-cljs only builds into the staticfiles tree. 227 CLJS tests, 31 Django tests, green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 01:11:41 -04:00
<script src="{% static 'mediapipe/vision_bundle.js' %}"></script>
<script src="{% static 'arthur/js/main.js' %}?v={{ js_version }}"></script>
Port steps 0-1: scaffold, the oracle, and the pure bottom Scaffolds frontend/ (shadow-cljs, reagent 1.2.0, re-frame 1.4.3) and ports everything below the data model, with the JS kept as a numeric oracle. domain/landmarks index tables, verbatim domain/ring subsample, offset, simplicity domain/geom similarity fit, procrustes, moving average domain/raster indexed scanline fill, stencil, disc, rect domain/palette the ramp, and the no-sampled-RGB rule 58 tests, 166 assertions. Parity with js/ on the identical 72-frame synthetic track: fit-similarity, procrustes-mean, fit-residual, moving-average, smooth-transforms, offset-ring and subsample-slots to 1e-9; the raster pixel-for-pixel over the whole buffer. Three deviations from the JS, each for a reason: - synth.cljs jitters from a SEEDED generator, not Math.random. Parity is only checkable if both sides can be handed the same track, and a failing assertion has to be reproducible. `:rand-fn` takes the generator over, so oracle.mjs stubs js/Math.random and js/ itself stays untouched. - raster/->rgba replaces toImageData. ImageData is a DOM type and domain/ may not touch the DOM; returning plain bytes also lets the no-intermediate-colours assertion run in node. ui/canvas wraps it later. - offset-ring lives in domain/ring, not domain/geom, per architecture.md: it is an operation on an ordered traversal, not on a transform. Step 1's "done" also names the swapped-iris vote, but pairIrises is in pipeline.js and belongs to step 7. The precondition is asserted instead -- `:swap-iris` really does move both blocks -- so the vote will have a track that disagrees with it when it arrives. One finding, recorded in full in the test that measures it: smooth-transforms buys nothing on the synthetic track. Against jitter-free ground truth, radius 1 helps by 17% on one noise realisation and hurts by 0.5% on another, so its benefit is within noise; from radius 2 up the cost is unambiguous, and by radius 5 the filter is below the true motion's own high-frequency energy, i.e. smoothing away performance. The test pins the shape of the knob rather than a preferred value. This may say more about the synth's jitter being unrealistically small (+/-0.001 normalised) than about the knob; step 6 settles it on real footage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 14:43:34 -04:00
</body>
</html>