Skip to content

Add the signal core: five shapes, features, one-way seed derivation - #9

Merged
guillaumelauzier merged 1 commit into
mainfrom
claude/generatedart-backend-fixes-dmhq3v
Sep 5, 2026
Merged

Add the signal core: five shapes, features, one-way seed derivation#9
guillaumelauzier merged 1 commit into
mainfrom
claude/generatedart-backend-fixes-dmhq3v

Conversation

@guillaumelauzier

Copy link
Copy Markdown
Contributor

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.

Shape Examples
time_series heart rate, glucose, steps, spend
interval_series sleep stages, calendar events, trips
point_events songs played, messages, transactions
spatial_trace a run, a commute, a year of travel
categorical genres, merchants, contacts

The 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.sqldata_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. Includes masd (mean absolute successive difference) 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. 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. featureDigest deliberately 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:

  • every shape validates, and each rejection path is covered
  • feature extraction is deterministic; masd separates a jagged series from a smooth one with identical mean
  • canonical encoding is order-independent and rounds away float noise
  • the seed leaks no feature value — asserted directly, since the seed is published on-chain and pinned to IPFS forever
  • a near-identical night yields a completely different seed
  • rotating the salt changes the seed but not the verifiable digest
  • derivation refuses a missing or weak salt

70/70 tests pass (48 existing unaffected), npm run typecheck clean, 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

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
@cloudflare-workers-and-pages

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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
guillaumelauzier marked this pull request as ready for review September 5, 2026 19:12
@guillaumelauzier
guillaumelauzier merged commit 00fd139 into main Sep 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants