Commit Graph
42 Commits
Author SHA1 Message Date
ekklesia 88038476b3 fix(images): bound external runtime processing
CI / lint (pull_request) Successful in 11s
CI / typecheck (pull_request) Successful in 10s
CI / test (pull_request) Successful in 16s
CI / build (pull_request) Successful in 10s
CI / integration (pull_request) Failing after 2m14s
2026-10-10 10:54:33 -05:00
ekklesia 871d198221 ci: widen display upload deadline
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 13s
CI / test (pull_request) Successful in 18s
CI / build (pull_request) Successful in 14s
CI / integration (pull_request) Failing after 1m57s
2026-10-09 22:37:50 -05:00
ekklesia c28e4b2de7 ci: restore external package integration
CI / lint (pull_request) Successful in 12s
CI / typecheck (pull_request) Successful in 13s
CI / test (pull_request) Successful in 16s
CI / build (pull_request) Successful in 10s
CI / integration (pull_request) Failing after 1m57s
2026-10-09 22:30:52 -05:00
Joey 6233de0e5c Merge pull request 'build: package signage with ekklesia 0.2.3' (#12) from feat/native-package-0.2.3 into main
CI / lint (push) Successful in 11s
CI / typecheck (push) Successful in 10s
CI / test (push) Successful in 16s
CI / build (push) Successful in 12s
CI / integration (push) Skipped
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/12
Reviewed-by: Joey <[email protected]>
2026-10-10 00:39:25 +00:00
ekklesia 6fca109375 build: package signage with ekklesia 0.2.3
CI / lint (pull_request) Successful in 10s
CI / typecheck (pull_request) Successful in 11s
CI / test (pull_request) Successful in 17s
CI / build (pull_request) Successful in 12s
CI / integration (pull_request) Skipped
2026-10-09 19:35:45 -05:00
The_River e7f7404d35 Merge pull request 'feat: build Signage on its own from the published Ekklesia 0.2.0 packages' (#11) from feat/standalone-build into main
CI / lint (push) Successful in 10s
CI / typecheck (push) Successful in 10s
CI / test (push) Successful in 15s
CI / build (push) Successful in 12s
CI / integration (push) Skipped
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/11
Reviewed-by: The_River <[email protected]>
2026-10-09 02:18:39 +00:00
ekklesiaandClaude Opus 5.5 33bb146349 feat: build Signage on its own from the published Ekklesia 0.2.0 packages
CI / lint (pull_request) Successful in 11s
CI / typecheck (pull_request) Successful in 11s
CI / test (pull_request) Successful in 17s
CI / build (pull_request) Successful in 12s
CI / integration (pull_request) Skipped
Signage no longer needs an Ekklesia checkout. A clean clone installs with
`pnpm install --frozen-lockfile` and runs lint, typecheck, test, build, and validate by itself, taking
Ekklesia's module-development packages from Ekklesia's public registry (ADRs 0053 and 0054).

- Dependencies: every `workspace:*` becomes `^0.2.0`. @ekklesia/module-pack is added as a
  devDependency pinned exactly to 0.2.0, as ADR 0053 requires; it is not invoked yet. No
  @ekklesia/contracts dependency. drizzle-orm stays a direct dependency, satisfying the adapter's
  optional peer.
- .npmrc points the @ekklesia scope at
  https://git.riverofglorymn.org/api/packages/River_of_Glory_Church/npm/ with no credential, because
  reading needs none.
- pnpm-workspace.yaml holds this repository's pnpm settings only:
  - `allowBuilds: esbuild: true`, as the platform's standalone fixture does;
  - exact-version minimumReleaseAgeExclude entries for the 0.2.0 train, which pnpm 11 otherwise holds
    back on its release day;
  - the platform's narrowly scoped override for GHSA-67mh-4wv8-2f99 (esbuild via drizzle-kit), which
    Signage inherited while it built inside Ekklesia's workspace.
- engines.node `>=24.11.0 <25` and packageManager `[email protected].0`, both from Ekklesia 0.2.0. Node
  24.18.0 goes in .node-version for CI, as in Ekklesia.
- biome.json carries the platform's Biome configuration, which `biome lint` used to pick up from the
  workspace root, so lint rules are unchanged.
- pnpm-lock.yaml is Signage's own: registry tarballs with integrity hashes, and no workspace or file
  links.
- `prepack` runs this package's own build instead of the workspace root's turbo.
- `pnpm validate` (scripts/validate-module.mjs) checks the built package and its migrations with the
  published SDK parsers only:
  - the manifest, its identity, and its entrypoints;
  - that the built runtime's id, version, and database ABI match the manifest;
  - that the built schema metadata is plain, namespaced, and has module-internal foreign keys;
  - the SDK compatibility range;
  - that the migration journal and policy agree.

  Core's own `modules:validate` and `migrations:validate` are not published. What only an Ekklesia
  installation can check is named in the script.

Removed: ci/assemble.sh, ci/platform.env, ci/pnpm-lock.yaml, ci/update-lock.sh. The lint, typecheck,
test, and build jobs check out only this repository; build also runs `pnpm validate`. The integration
job stays disabled. Its first step is now an explicit placeholder rather than the deleted assembly,
and its explanation says it returns when Signage is a canonical linux-x64-glibc Module Package
installed into Ekklesia's generic image.

No source, behaviour, schema, or version change: Signage stays 0.1.2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:49:17 -05:00
The_River fefe34be03 Merge pull request 'feat: move Signage to the Ekklesia 0.2 module contract (manifest v8, database ABI v1)' (#10) from feat/platform-0.2-compat into main
CI / lint (push) Successful in 14s
CI / typecheck (push) Successful in 15s
CI / test (push) Successful in 20s
CI / build (push) Successful in 19s
CI / integration (push) Skipped
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/10
Reviewed-by: The_River <[email protected]>
2026-10-09 01:41:35 +00:00
ekklesiaandClaude Opus 5.5 a6d553efbb feat: move Signage to the Ekklesia 0.2 module contract (manifest v8, database ABI v1)
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 16s
CI / test (pull_request) Successful in 21s
CI / build (pull_request) Successful in 20s
CI / integration (pull_request) Skipped
Signage moves from the legacy module contract to Ekklesia 0.2.0's, with no behaviour or schema
change:

- manifest version 8, `database: { abi: 1 }`, compatibility ^0.2.0 for platform and module SDK;
  the runtime declares the same ABI, as Core requires;
- every store reaches PostgreSQL through the platform's Module Database ABI v1 executor, with
  Signage's own Drizzle tables and the module-side adapter (`moduleDrizzle`) from
  @ekklesia/module-database. createSignageStore takes the executor, so no call site changes;
- the schema entrypoint exports only plain ABI v1 metadata (table names and foreign-key targets),
  derived from tables.ts with moduleDrizzleSchemaMetadata; no Drizzle object reaches Core;
- duplicate-image detection reads SQLSTATE 23505 from the stable Ekklesia database error, found
  with findEkklesiaDatabaseError through Drizzle's DrizzleQueryError wrapping. The old
  `error.code === "23505"` check never matched a wrapped error, so a duplicate-upload race
  surfaced as a 500 instead of the content-hash conflict.

tables.ts declarations, the migration, and its pinned checksum are unchanged.

Tests: an ABI v1 executor double that enforces Core's scope, parameter, and abort rules drives all
13 store transactions, their rollbacks, raw int8/timestamptz/jsonb decoding, and the 23505
regression, including generic and driver-shaped errors that must not count as duplicates.

CI, temporarily: the platform pin moves to v0.2.0 (1788d17) with a regenerated ci/pnpm-lock.yaml.
The integration job is disabled: from 0.2.0 the platform image is generic (ADR 0048) and installs
modules only as Module Packages, which Signage cannot be until its sharp dependency goes through
the native package flow. The job and ci/integration.sh stay, and the job returns in that change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 20:36:11 -05:00
The_River 86ae907f59 Merge pull request 'chore: release signage 0.1.2' (#9) from chore/release-0.1.2 into main
CI / lint (push) Successful in 12s
CI / typecheck (push) Successful in 14s
CI / test (push) Successful in 20s
CI / build (push) Successful in 19s
CI / integration (push) Successful in 5m41s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/9
Reviewed-by: The_River <[email protected]>
v0.1.2
2026-10-06 03:51:14 +00:00
ekklesiaandClaude Opus 5.5 855b5f9f54 chore: release signage 0.1.2
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 15s
CI / test (pull_request) Successful in 21s
CI / build (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 6m0s
0.1.2 fixes what the first production Signage deployment found:
- #4: the display and server share one message schema; every heartbeat
  from an image slide used to be refused with a 400;
- #5: every display request retries with backoff and never gives up, so
  a display that failed one request no longer stays offline until
  reloaded;
- #6: one object URL per cached slide, released when done; the leak
  exhausted the browser within minutes;
- CI (#7, #8): every change runs inside a pinned Ekklesia, end to end
  in a real browser, and cleans up even when killed.

compatibility.platform stays ^0.1.0: every fix is module-side, and CI
tested it against platform v0.1.1 plus test-only scripts. The README no
longer says Signage is unreleased or undeployed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 22:08:31 -05:00
The_River e6c6553afc Merge pull request 'ci: the integration job removes its stacks even when the runner kills it' (#8) from ci/stack-cleanup into main
CI / lint (push) Successful in 14s
CI / typecheck (push) Successful in 15s
CI / test (push) Successful in 22s
CI / build (push) Successful in 21s
CI / integration (push) Successful in 5m43s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/8
Reviewed-by: The_River <[email protected]>
2026-10-06 02:58:11 +00:00
The_River 525335e6dd Merge pull request 'fix(display): keep one object URL per cached slide, and give them back' (#6) from fix/display-media-object-urls into main
CI / lint (push) Successful in 12s
CI / typecheck (push) Successful in 14s
CI / test (push) Successful in 21s
CI / build (push) Successful in 18s
CI / integration (push) Canceled after 1m3s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/6
Reviewed-by: The_River <[email protected]>
2026-10-06 02:56:01 +00:00
ekklesiaandClaude Opus 5.5 e3c5e546eb ci: run stream-recycle on every change
CI / lint (pull_request) Successful in 13s
CI / typecheck (pull_request) Successful in 15s
CI / test (pull_request) Successful in 20s
CI / build (pull_request) Successful in 19s
CI / integration (pull_request) Successful in 5m41s
It failed on main before this PR: the media cache leaked one blob per
slide per refresh until the browser refused the display's own requests.
With one object URL per slide it holds three live URLs for three slides,
and CI now keeps it there, together with "stays live for the whole watch".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:45:28 -05:00
ekklesiaandClaude Opus 5.5 607f375809 fix(display): keep one object URL per cached slide, and give them back
The Cache Storage media cache minted a new object URL on every lookup
and never revoked any. Every state refresh looks up every cached slide,
so a running display pinned one more blob per slide per refresh until
the browser refused its requests with net::ERR_INSUFFICIENT_RESOURCES.
It then showed its offline banner on a working network. Two tabs leaked
twice as fast, and real photographs fill blob storage far sooner than
test slides.

Each cache entry now has exactly one object URL:
- every lookup reuses it;
- pruning or replacing the entry revokes it;
- release(), new on SignageMediaCache, revokes all of them, and the
  controller calls it on stop once the stage is gone.

Two lookups of one slide racing each other keep the first URL and make
no second one. The in-memory cache has nothing to give back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:45:19 -05:00
ekklesiaandClaude Opus 5.5 1c6c9ccc1f test(display): one live object URL per cached slide, released when done
The media cache minted a new object URL on every lookup and never
revoked one. Each state refresh looks up every cached slide, so a
display leaked one pinned blob per slide per refresh. In a real browser
(Ekklesia draft PR #56, scenario stream-recycle) its own state requests
then failed with net::ERR_INSUFFICIENT_RESOURCES within about four
minutes, and it showed its offline banner on a working network.

Fails today: 303 live URLs for 3 slides after 100 refreshes. The same
bytes get a new URL each time, nothing is revoked on prune or replace,
and there is no release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:45:19 -05:00
The_River ab4142fb95 Merge pull request 'fix(display): retry every failed request with backoff, and never give up' (#5) from fix/display-recovery into main
CI / lint (push) Successful in 13s
CI / typecheck (push) Successful in 14s
CI / test (push) Successful in 20s
CI / build (push) Successful in 18s
CI / integration (push) Successful in 3m35s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/5
Reviewed-by: The_River <[email protected]>
2026-10-06 02:44:20 +00:00
ekklesiaandClaude Opus 5.5 bf4e98fa30 ci: run session-blip and stream-failure on every change
CI / lint (pull_request) Successful in 12s
CI / typecheck (pull_request) Successful in 14s
CI / test (pull_request) Successful in 22s
CI / build (pull_request) Successful in 18s
CI / integration (pull_request) Successful in 3m40s
Both failed on main before this PR: a display that failed its first
viewer-session request, or its first realtime connection, never
recovered without a reload. With the retries in place they pass, and
CI now holds them there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:32:03 -05:00
ekklesiaandClaude Opus 5.5 fbc8af7510 fix(display): retry every failed request with backoff, and never give up
A display tried each of its network paths once:
- A registration that failed left it continuing from its persisted
  state, behind the offline banner, with no realtime, heartbeat or
  retry, until someone reloaded it. This is what the production kiosk
  showed after a restart.
- A failed state refresh scheduled no further refresh, so a display
  whose realtime was also down never fetched state again.
- A realtime stream the host reported closed was never subscribed
  again.

Each path now retries on its own backoff: about 1 s, doubling to a
one-minute ceiling, with jitter so a building's screens do not return
in lockstep. There is no attempt limit, because a television has
nobody to press Retry. Each resets when its request works again.
- Registration retries until it works, then brings the display live
  exactly as a first-time success does.
- A failed refresh schedules its own retry, with no realtime needed.
- A closed realtime subscription is replaced.
- Stopping clears every retry.

The recovery test's virtual window shrinks from an hour to five
minutes: eight failures back off for at most about 183 s, and an hour
of offline-continuation timers made the test slow without proving more.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:31:55 -05:00
ekklesiaandClaude Opus 5.5 518fd101a2 test(display): a display recovers from every failed request without a reload
Pins what a kiosk needs from each network path, on a virtual clock: a
registration that fails once is retried with backoff until it works, a
state refresh that fails is retried without a realtime nudge, and a
realtime stream the host reports closed is subscribed again. Nothing is
scheduled after stop.

Fails today on all three paths: each is tried once. The first matches
the production screen exactly: mounted, showing "Offline — continuing
from the last update", with no heartbeat and no retry until someone
reloads it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:31:49 -05:00
The_River 27ee279f28 Merge pull request 'fix(display): define the display-server messages once, in src/shared' (#4) from fix/display-protocol-contract into main
CI / lint (push) Successful in 14s
CI / typecheck (push) Successful in 14s
CI / test (push) Successful in 20s
CI / build (push) Successful in 18s
CI / integration (push) Successful in 3m28s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/4
Reviewed-by: The_River <[email protected]>
2026-10-06 02:30:41 +00:00
ekklesiaandClaude Opus 5.5 f1b7d67aa5 fix(display): define the display-server messages once, in src/shared
CI / lint (pull_request) Successful in 13s
CI / typecheck (pull_request) Successful in 15s
CI / test (pull_request) Successful in 21s
CI / build (pull_request) Successful in 18s
CI / integration (pull_request) Successful in 3m32s
The display controller and the viewer routes each described the
heartbeat their own way and disagreed about one thing: the display sent
null for "nothing to report", and the route accepted only a missing
field. Every heartbeat from a screen showing a native image was refused
with a 400. The roster therefore marked working screens Offline 90 s
after they opened, and never recorded offlineReady or cachedAssetCount.

src/shared/display-protocol.ts now defines every message between a
display and the server:
- viewer session request and response;
- heartbeat request and response;
- channel state;
- the realtime display payload.

Both halves import it. Its rules: a nullable field accepts null
explicitly, an absent field means "not reported", and an unknown field
is ignored, so a newer display is never refused by an older server.

- The routes parse with the shared parsers, and a refusal names the
  field with Signage's sanitized code.
- The server's channel state extends the wire type, so a field the
  display relies on cannot drift without a compile error.
- The display builds its heartbeat as the shared type, and reads its
  registration with the shared parser instead of casting it.
- Test stubs that described server responses more loosely than the
  server sends them (no viewerName, no stale, partial media) now match
  the real responses and are typed as the protocol.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:25:21 -05:00
ekklesiaandClaude Opus 5.5 458043280a test(display): run the real display controller against the real viewer routes
Every display test answered the controller with a hand-written stub, and
every viewer test called the service with a hand-written object, so
neither side was ever tested against the other. In production every
heartbeat was refused with a 400.

This suite answers the real controller's requests with the real viewer
route handlers over the in-memory store. The routes gain an optional
store factory for that; production still uses the module database.

Fails today: POST /viewers/:viewerId/heartbeat -> 400
SIGNAGE_INVALID_REQUEST, because the display sends
reportedContributionId: null and the route accepts only a missing field.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:25:21 -05:00
ekklesiaandClaude Opus 5.5 6869ae433a ci: pin the platform at the merged sweep, 874deab on main
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 14s
CI / test (pull_request) Successful in 19s
CI / build (pull_request) Successful in 18s
CI / integration (pull_request) Successful in 3m49s
Ekklesia #60 was rebase-merged, so the PR head 00e5441 never became part
of main, and ci/assemble.sh rightly refuses it. 874deab is #60's merge on
main. ci-sweep.sh, ci-project.sh and smoke-deploy.sh are byte-identical
to the old pin, no package file changed, and the assembled lockfile
still passes a frozen install.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:19:57 -05:00
ekklesiaandClaude Opus 5.5 cbf2a8ee39 ci: the integration job removes its stacks even when the runner kills it
CI / typecheck (pull_request) Failing after 3s
CI / test (pull_request) Failing after 4s
CI / build (pull_request) Failing after 4s
CI / integration (pull_request) Skipped
CI / lint (pull_request) Failing after 3s
The platform's rule (its ci-sweep.sh, docs/development/security.md),
applied to the one Signage job that starts Docker stacks:
- it has its own EKKLESIA_CI_JOB_KEY;
- it sweeps stale CI stacks from the runner before it starts;
- it removes its own stacks in a final `if: always()` step, which
  tolerates a failed assembly, since nothing was started then;
- the image ci/integration.sh builds is named and labeled as a CI stack
  of this run and job, with the time CI built it, so the sweep finds it.

ci/platform.env moves to the platform commit that adds the sweep
(Ekklesia #60). The assembled lockfile is unchanged, and still passes a
frozen install.

Verified: SIGKILL of the integration script's whole process group
mid-run left 11 resources (2 containers, 4 networks, 4 volumes and the
image). `ci-sweep.sh --job` removed exactly those 11. A normal run
passes, and the job sweep after it finds nothing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 21:05:15 -05:00
ekklesiaandClaude Opus 5.5 cfa4628f8c test(ci): the integration job must clean up its stacks even when killed
The platform leaked a whole CI stack for two days: a job the runner
kills never runs its cleanup trap. The platform's rule now applies here:
the job that starts Docker stacks (integration) has its own
EKKLESIA_CI_JOB_KEY, sweeps stale CI stacks first, removes its own in a
final if: always() step, and labels the image it builds.

ci/check-cleanup.sh checks that, and the lint job runs it. It fails
today on all five counts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 20:58:04 -05:00
The_River 914034dda0 Merge pull request 'ci: build, test, and run Signage inside a pinned Ekklesia on every change' (#7) from ci/gitea-actions into main
CI / lint (push) Successful in 13s
CI / typecheck (push) Successful in 14s
CI / test (push) Successful in 20s
CI / build (push) Successful in 20s
CI / integration (push) Successful in 3m22s
Reviewed-on: https://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/7
Reviewed-by: The_River <[email protected]>
2026-10-06 00:38:49 +00:00
ekklesiaandClaude Opus 5.5 3073baeb8b ci: build, test, and run Signage inside a pinned Ekklesia on every change
CI / lint (pull_request) Successful in 12s
CI / typecheck (pull_request) Successful in 15s
CI / test (pull_request) Successful in 19s
CI / build (pull_request) Successful in 18s
CI / integration (pull_request) Successful in 3m34s
Signage had no CI. Its tests passed while three defects shipped that
appear only inside Ekklesia, in a real browser. Every job now assembles
Signage into a pinned platform commit, as an instance does, and
installs that workspace with --frozen-lockfile:
- lint, typecheck, test: Signage's own checks;
- build: the platform's gates against exactly this module set (boundary
  rules, module manifest, migrations), then the build;
- integration: the deployment image from the platform's Dockerfile,
  then the platform's split-topology smoke. It pairs a kiosk display,
  restarts it on the same profile, and runs the scenarios in
  ci/display-scenarios.

The supporting files:
- ci/platform.env pins one platform commit. ci/assemble.sh fetches the
  platform's main and refuses a pinned commit that is not on it, so CI
  only runs reviewed platform code.
- ci/pnpm-lock.yaml is the assembled workspace's lockfile, regenerated
  with ci/update-lock.sh.

Same trust rules as the platform: actions pinned by SHA,
persist-credentials false, no secrets, no pull_request_target.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 19:11:53 -05:00
The_River 5e418bf941 Merge pull request 'feat: align Signage with platform display and design' (#3) from fix/signage-playback-and-displays into main
Reviewed-on: http://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/3
v0.1.1
2026-10-01 21:36:31 +00:00
The_River a68d7ebaee Update README.md 2026-10-01 21:32:21 +00:00
ekklesia 1bf89a3942 feat: align Signage with platform display and design 2026-09-30 18:21:42 -05:00
The_River ac2b8ded93 Merge pull request 'Fix/signage playback and displays' (#2) from fix/signage-playback-and-displays into main
Reviewed-on: http://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/2
2026-09-30 02:01:22 +00:00
ekklesia af0f0adadc test: align signage release expectations 2026-09-29 20:50:02 -05:00
ekklesia 36c1a72363 feat: make display setup actionable 2026-09-29 20:49:51 -05:00
ekklesia 7999582460 fix: preserve valid timeline slot positions 2026-09-29 20:49:39 -05:00
ekklesia 218e05e71e chore: release signage 0.1.1 2026-09-29 19:33:56 -05:00
ekklesia afac1bf8e7 chore: release signage 0.1.1 2026-09-29 19:27:03 -05:00
The_River 49d056f744 Merge pull request 'Fix/signage stabilization' (#1) from fix/signage-stabilization into main
Reviewed-on: http://git.riverofglorymn.org/River_of_Glory_Church/Ekklesia_Signage/pulls/1
2026-09-29 23:57:32 +00:00
ekklesia aa8cb1c96f fix: complete display and channel group management 2026-09-29 18:15:26 -05:00
ekklesia 5760059ee3 fix: make timeline activation durable and visible 2026-09-29 18:15:08 -05:00
ekklesia 92bef4c7aa feat: initial Signage 0.1.0 2026-08-28 16:41:54 -05:00
ekklesia 20756f11af first commit 2026-08-27 17:05:45 -05:00