Skip to content

Adopt this repository into the paranext organization - #1

Open
tjcouch-sil wants to merge 16 commits into
mainfrom
adopt-into-paranext-org
Open

Adopt this repository into the paranext organization#1
tjcouch-sil wants to merge 16 commits into
mainfrom
adopt-into-paranext-org

Conversation

@tjcouch-sil

@tjcouch-sil tjcouch-sil commented Aug 31, 2026

Copy link
Copy Markdown
Member

Seeds this repo as the paranext organization's source of truth for the editor packages, and documents what that means for everyone who works here or consumes it.

Part of PT-4500.

What this does

Ownership and provenance. Adds a Where this repository lives section: this is a standalone copy of eten-tech-foundation/scripture-editors carrying full history, MIT license, and attribution — deliberately not a GitHub fork, because while a repo is a fork GitHub always defaults new PRs to the parent and offers no setting to change that. Adds a SIL Global / United Bible Societies copyright line alongside ETEN Tech Foundation's; the license stays MIT.

Relationship to paranext-core. Documents that paranext-core does not install these from npm — its preinstall builds them from source and stages them, and its package.json files reference the staged folders with file: specifiers. The consequence worth knowing: this repo's package.json files are authoritative for its own dependencies, so adding or bumping one here flows into paranext-core with nothing to restate on the consuming side.

The platform-yalc branch. Explains why it still exists — the name is historical (paranext-core no longer uses yalc), but its coordination role is real: a breaking change lands on main first and platform-yalc moves forward only once the consuming side is ready.

The eten-tech-foundation remote. How to add it (named eten-tech-foundation, not upstream — we don't track it), pull from it, and contribute back. We may never use it, but the option is deliberately kept open.

Transferring work. docs/transferring-work-from-eten-tech-foundation.md — this repo was seeded with a curated ref set (main, platform-yalc, release-prep, and all 80 release tags), so feature branches were left behind. The guide covers moving branches, tags, and open PRs across, plus re-pointing an existing clone non-destructively.

Release process

Replaces the npm-publishing machinery with a process modeled on paranext-core's. Consumers pin a revision of this repository, not a published version, so a release is a repository tag and one tag pins both packages. Tags are v<platform-editor version>.

  • scripts/bump-versions.ts + bump-versions workflow and composite action — sets both package versions on a bump-versions-<version> branch. Tested locally; it moves both manifests and correctly leaves the workspace: specifier alone.
  • publish workflow — verifies the requested version matches packages/platform/package.json before tagging (a tag disagreeing with the manifest would pin consumers to a build claiming a different version), creates the release, and can open the follow-up bump branch.
  • Removes the scribe publish workflow, the tag-triggered npm publish steps, and the auto-bump-after-publish job. test-publish.yml becomes test.yml, a pure CI workflow.

Nx Cloud references are dropped — no account for this org, and the caching doesn't earn standing one up. It was already commented out in the test workflow ("credits exhausted").

Notes for review

  • Nothing here changes the editor's architecture, public API, or package names. Package names stay @eten-tech-foundation/*; renaming was explicitly out of scope.
  • packages/scribe removal is a separate PR so the diff against upstream stays easy to reason about.
  • The release process is the piece I'd most like a second opinion on — in particular the choice to tag v<platform-editor version> at the repo level rather than keeping per-package platform_v* / utilities_v* tags. Since consumption pins one repo revision, one tag pinning both packages seemed the honest unit, but it's a judgment call.
  • The workflows are not exercised by this PR. bump-versions and publish are workflow_dispatch-only and want a real dispatch before anyone relies on them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VCWPCf35QD5sTkFghsTfUX


This change is Reviewable

This repo is now maintained at paranext/scripture-editors and is the source of truth
for the editor packages. It carries the full history of
eten-tech-foundation/scripture-editors, its MIT license, and its attribution, but it is
deliberately not a GitHub fork: while a repository is a fork, GitHub always defaults new
pull requests to the parent and offers no setting to change that.

Documents what that means in practice:

- Where this repository lives, and its relationship to paranext-core — which builds these
  packages from source and stages them rather than installing them from npm, so this
  repo's package.json files are authoritative for its own dependencies.
- Why the platform-yalc branch still exists. The name is historical, but its coordination
  role — letting a breaking change land on main before build servers pick it up — is not.
- How to set up the eten-tech-foundation remote, pull from it, and contribute back, so
  that option survives even though we do not track it.
- How to move branches, tags, and open PRs across from the other repository, since this
  one was seeded with a curated ref set rather than a full mirror.

Replaces the npm-publishing machinery with a release process modeled on paranext-core's.
Consumers pin a revision of this repository, not a published version, so a release is a
repository tag and one tag pins both packages. Adds a bump-versions script, workflow, and
composite action, plus a publish workflow that verifies the requested version matches the
manifest before tagging. Removes the scribe publish workflow and the tag-triggered npm
publish and auto-bump jobs.

Drops the Nx Cloud references. There is no account for this organization, and the caching
does not earn standing one up.

Adds a SIL Global and United Bible Societies copyright line alongside ETEN Tech
Foundation's. The license stays MIT, which is what keeps contributing back possible.
…pushes

paranext-core's package-lock.json records these packages' dependency closure, so a
dependency added, removed, or re-ranged here breaks every core build the moment
platform-yalc moves — core's `npm ci` refuses to run until its lockfile is refreshed.
That refresh is a one-command PR in core, but nothing reminded the person moving the
branch to open it.

Adds a workflow that runs on every push to platform-yalc: it recomputes what core's
lockfile should record from this repo's manifests (applying the same transforms core's
staging applies — devDependencies dropped, workspace: specifiers rewritten to the sibling
staged folder, the package list read from core's own dev-packages.json so the repos
cannot drift) and passes when core's main matches. When it does not, it scans open core
PRs touching package-lock.json for one that brings it in sync — the refresh being in
flight is fine — and otherwise fails with the exact commands to fix it. Version-only
bumps pass untouched; npm ci does not check the version of a file: dependency, so
releases need no core PR.

The README's platform-yalc section documents the same flow for humans.

Verified against live data: the in-sync path against the core PR branch, and a fabricated
dependency change exercising the mismatch diff, the open-PR scan (152 open PRs, paginated),
and the failure instructions.
@tjcouch-sil
tjcouch-sil force-pushed the adopt-into-paranext-org branch from ec221be to 75adc47 Compare September 1, 2026 22:27
The workflow catches a missing core lockfile PR after the push; this catches it before.
Operates on a temporary branch from origin/platform-yalc (never the often-stale local
branch), restores the original checkout on exit, aborts cleanly on rebase conflicts, and
runs the same verify-consumer-lockfile-sync check the CI workflow runs. --dry-run stops
short of pushing; --skip-verify is the documented emergency hatch.
@tjcouch-sil
tjcouch-sil force-pushed the adopt-into-paranext-org branch 2 times, most recently from 75adc47 to b3b88d4 Compare September 2, 2026 14:47
paranext-core consumes these packages by copying them out of a checkout rather than
installing them from a registry, so until now every core developer needed this repo's
toolchain — pnpm, nx, a full vite build and api-extractor run — during `npm install`, just
to run Platform.Bible. That build is also where a whole class of install failures lives.

Committing dist reduces the consumer's work to a copy. Only people changing the editor
build it.

The obligation this creates is that dist can go stale against src, and a stale dist is
invisible in review: the source diff looks right while consumers get old code. CI now
rebuilds and fails if the committed output differs (scripts/verify-committed-dist.mjs).
Every published artifact is byte-deterministic — verified by rebuilding and comparing
hashes — so the check is exact rather than advisory.

Also stops packing dist/*.tsbuildinfo. That is TypeScript's incremental cache, the one
non-deterministic file in the build, and it was being copied into consumers' staged copies
as though it were a shipped artifact.
@tjcouch-sil

Copy link
Copy Markdown
Member Author

Added: the built dist/ is now committed (closes #4, brought into this round rather than deferred).

packages/platform/dist and packages/utilities/dist are tracked, so paranext-core consumes these packages by copying rather than building — no pnpm, no nx, no build during its npm install. Measured on a checkout that has the committed dist: ~7 seconds instead of several minutes, and the whole Volta/pnpm class of install failures leaves the consumer path entirely.

Guarding the obvious risk (a stale dist is invisible in review):

  • scripts/verify-committed-dist.mjs + a CI step that rebuilds and fails on any drift.
  • Determinism verified before relying on it — rebuilt and compared hashes; every published artifact is byte-identical.
  • The one non-deterministic file was dist/*.tsbuildinfo, which turned out to be TypeScript's incremental cache being packed into consumers as though it were a shipped artifact. Now excluded from both git and files.
  • README documents the rebuild-and-commit obligation.

Two things reviewers should know:

  1. nx caches dist as a target output, so a cache hit restores nx's copy over the committed one and can delete files the cache predates — I hit exactly that (17 spurious deletions). paranext-core's staging builds with --skip-nx-cache. If you see unexplained dist deletions locally, git checkout -- packages/*/dist.
  2. Until this merges and platform-yalc rebases past it, core still builds via its fallback path — verified working, so there is no window where core breaks.

A --stat summary cannot distinguish a genuine source change from a build that is not
reproducible across machines, which is exactly the question a CI failure here raises.
nx declares `dist` a cached target output, and earlier steps in this job populate that
cache, so a cache hit restores nx's copy over the committed one — the check would then be
grading the cache instead of the source. It also explains an earlier spurious failure of
this step: the first run after committing dist rebuilt over it incrementally.
…tput

The first dist commit captured a mid-build state: tsc's per-file declarations under
dist/converters/ plus an index.d.ts that merely re-exported them. api-extractor consumes
those and emits the rolled-up API surface as index.d.ts, then removes them — so which
files exist depended on which nx targets last ran, and CI's freshness check failed
against a local build for exactly that reason.

Commits what a canonical build leaves: the rolled-up index.d.ts and the bundles. The
intermediate per-file declarations are gitignored, since whether they are present is not
meaningful and nothing references them — `types` resolves to dist/index.d.ts.
Same class of intermediate artifact: api-extractor rolls the declarations into
dist/index.d.ts and the map beside them becomes meaningless, but a plain `build` leaves it,
so its presence depends on which targets last ran. CI caught it as an untracked file.
tjcouch-sil and others added 7 commits September 3, 2026 11:15
`homepage`, `repository`, and `bugs` still named eten-tech-foundation, so
everything npm derives from them — the repository link on npmjs.com, `npm
repo`, and the "report an issue" link — sent people to the repository this
one replaces.

The package names and their `@eten-tech-foundation` scope are deliberately
unchanged: publishing continues under that scope, and renaming would break
every consumer. Only the location of the source moves.

`packages/scribe` is left alone; #2 removes that package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jvJ2eYTfxJaBkKoQbVeiW
`publish.yml` created the release tag after nothing but a build. The workflow
it replaced gated every publish on `needs: test`, and the committed-dist check
lives in `test.yml`, which only triggers on `main` — so a release cut from any
other branch was verified by nothing, and a tag cannot be un-cut. Run the same
checks `main` gets, all of them before the release step.

The bump step after the release could never have worked: `actions/checkout`
cleans the workspace, which takes `node_modules` with it, and the composite
action then runs `tsx`. By that point the tag exists and `bump-versions` is not
idempotent, so the failure has to be unpicked by hand. Reinstall first.

`verify-consumer-lockfile-sync.mjs` compared `dependencies` and
`peerDependencies` only. `peerDependenciesMeta` is what marks a peer optional,
so deleting an entry makes that peer required and npm pulls it into the
closure: verified against a staged `file:` dependency, making `yjs` required
this way fails every core `npm ci` with "Missing: yjs from lock file" while
both dependency lists read identically. Compare it too, structurally, since its
entries are objects.

That guard also spent one API request per open core PR — 154 today — to decide
which touch the lockfile, which exhausts the anonymous rate limit it runs under
locally. Look at recently-updated PRs first and walk the rest only when the
alternative is failing. A file list at the page limit now counts as a candidate
rather than being read as "no lockfile here", a deleted head fork is skipped
instead of throwing, the raw.githubusercontent reads authenticate like the API
reads, and an empty `CORE_BRANCH` falls back to `main` instead of building a
malformed URL.

`verify-committed-dist.mjs` capped its failure output at 200 lines, which
bounds nothing when `index.js.map` is three megabytes on one line. It also now
records the blind spot it cannot cover on its own — `git status` sees no
ignored path — and points at #5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
`nx.json` still carried ETEN's Nx Cloud access token and workspace id, disabled
only by an underscore on each key. Nx Cloud is off for this repo and the
credentials are not ours; remove them. Its `sharedGlobals` also named
`.github/workflows/ci.yml`, which this repo has never had, so no workflow
change has ever invalidated a cached target — point it at `test.yml`.

paranext-core consumes these packages by staging a built copy out of a
checkout, so `yalc` has no remaining role: drop the dependency and the
`devpub` scripts, and replace both "Develop in App" sections with the flow that
works now (`npm run build:editor` in core).

Badges pointed at the old organization and at `test-publish.yml`, which no
longer exists, and showed an npm version for packages that are not published to
npm — a repository tag is what pins them. Two docs named that workflow too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
`test.yml` ran on `main` only, so a release cut from `release-prep` or a
`hotfix-*` branch had no formatting, lint, typecheck or test run against it
anywhere. paranext-core covers those branches in its own test workflow and
therefore runs no checks at publish time; this repo had the triggers narrower
and the gap sat in the middle. Widen the triggers instead of duplicating the
checks into `publish.yml`.

The committed dist stays checked at publish, because core has no equivalent of
it: a tag is where consumers copy their editor from, a tag cannot be un-cut,
and a stale dist inside one looks like nothing at all. `extract-api` is the
build, so it replaces the separate build step rather than adding to it.

The post-release checkout now uses `clean: false`, matching core, rather than
reinstalling — and only runs when the bump ref actually differs from the
branch already checked out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
…efreshes

Deciding whether one open core PR touches `package-lock.json` costs an API
request, and core carries 150+ open at a time. Run locally by
`move-platform-yalc` there is no token, so the scan exhausted the 60/hour
unauthenticated limit and failed the check exactly when it mattered.

A lockfile refresh for the change being pushed is worked on alongside it, so
PRs untouched for two weeks are not it. `move-platform-yalc --skip-verify`
remains the escape hatch if that ever stops being true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
The failure told you to refresh core's lockfile and open a PR, which is right
when the refresh does not exist yet. It said nothing about the case where it
does and this did not find it — the PR has gone untouched past the search
window, or the two changes are being landed together on purpose — leaving the
escape hatch discoverable only by reading the script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
…d at

Core carries 155 open PRs, nearly all stale. Measured against it, a 7-day
window selects 51 and a 14-day window 60, against an unauthenticated budget of
60 requests an hour — 7 days fits with room for the listing calls, 14 does not.

The failure line also reported three bare numbers with no way to tell what they
counted, which read as though most PRs had been skipped. Say plainly how many
were examined, how many exist, and how many of those examined touch the
lockfile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqqozB7YdhQFin63R2gBpL
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.

1 participant