Add the signal core: five shapes, features, one-way seed derivation - #9
Merged
Merged
Conversation
First slice of the reposition toward generative art made from personal data. Backend only and not yet wired to routes — the pure core is worth getting right before source-specific parsing multiplies against it. Personal data streams are unbounded in variety but not in structure. Every one reduces to one of five shapes — time series, interval series, point events, spatial trace, categorical distribution — so an adapter's whole job is producing one of those, and everything downstream sees nothing else. Adding a source stays additive rather than multiplicative. The chain is: signal -> features -> seed = H(features || salt) -> the artwork, feeding the mint seed slot that already exists. - migrations/0021_signal_core.sql: data_sources, signal_imports, derivations, consent_records. There is deliberately no column anywhere that holds raw data, a sample series, or a feature vector; adding one would silently turn this into a health-data store under GDPR Article 9. Only the seed, a hash commitment to the features, and coarse descriptive metadata persist. - src/signals/shapes.ts: the five shapes and a strict validator. Strict because a malformed signal still produces a seed, and a seed gets minted permanently — it rejects millisecond timestamps, non-finite values, reversed intervals and out-of-range coordinates rather than repairing them. - src/signals/features.ts: pure statistical descriptors. Includes masd and fragmentation specifically because they carry the character of a signal — a restless night versus a still one — which is what makes a piece recognisably someone's rather than decorative. - src/signals/derive.ts: canonical encoding at fixed precision so the same data hashes identically across engines, plus a salted one-way derivation. The salt is required and fails closed: without it the feature space is small enough to brute-force a published seed back to someone's health data, so hashing alone would not be enough. 22 new unit tests over the core, including that the seed leaks no feature value, that a near-identical night yields a different seed, that rotating the salt changes the seed but not the verifiable digest, and that derivation refuses a weak salt. 70/70 tests pass, typecheck clean, migration applies locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuSa99HABSrWRarT9BQ5Tz
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
generatedart | 31cc773 | Commit Preview URL Branch Preview URL |
Sep 05 2026, 06:32 PM |
guillaumelauzier
marked this pull request as ready for review
September 5, 2026 19:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
First slice of the reposition toward generative art made from personal data. Backend only, not yet wired to routes — the pure core is worth proving before source-specific parsing multiplies against it.
Personal data streams are unbounded in variety but not in structure. Every one reduces to one of five shapes, so an adapter's entire job is producing one of those and everything downstream sees nothing else. Adding a source stays additive rather than multiplicative.
time_seriesinterval_seriespoint_eventsspatial_tracecategoricalThe chain:
signal → features → seed = H(features ‖ salt) → artwork, feeding the mint seed slot that already exists (0015_mint_seed.sql).Changes
migrations/0021_signal_core.sql—data_sources,signal_imports,derivations,consent_records.There is deliberately no column anywhere that holds raw data, a sample series, or a feature vector. Adding one would silently turn this into a health-data store subject to GDPR Article 9. Only the seed, a hash commitment to the features, and coarse descriptive metadata persist. The schema is the enforcement.
src/signals/shapes.ts— the five shapes plus a strict validator. Strict on purpose: a malformed signal still produces a seed, and a seed gets minted permanently. It rejects millisecond timestamps (the classic adapter bug — silently yields a window ~50,000 years wide), non-finite values, reversed or zero-length intervals, and out-of-range coordinates, rather than quietly repairing them.src/signals/features.ts— pure statistical descriptors, the irreversible narrowing step. Includesmasd(mean absolute successive difference) andfragmentationspecifically because they carry the character of a signal — a restless night versus a still one — which is what makes a piece recognisably someone's rather than decorative. Spatial features record bounding-box extent only, never coordinates, which would identify a home address.src/signals/derive.ts— canonical encoding at fixed precision so the same night hashes identically across engines (IEEE-754 differs in the last bits), plus a salted one-way derivation.The salt is required and fails closed. Without it the feature space is a dozen rounded numbers — small enough to brute-force a published seed back to plausible health data. Hashing alone is not sufficient here.
featureDigestdeliberately excludes the salt so a holder can verify a commitment without the platform's secret.Testing
22 new unit tests over the pure core — the highest-risk code in the pipeline:
masdseparates a jagged series from a smooth one with identical mean70/70 tests pass (48 existing unaffected),
npm run typecheckclean, and the migration applies locally (11 commands).Not in this PR
Source adapters, import UI, generators, and manufacturing. Routes are not mounted yet — this is the core only.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QuSa99HABSrWRarT9BQ5Tz
Generated by Claude Code