Finding · Published
A Git-native browser editor for Masters Findings: one candidate wins the workflow, the other wins the credential
Both candidate editors represent the Masters publication contract faithfully and lose nothing in a round trip. Only one produces a pull request automatically — but only the other can hold a credential scoped to a single repository, and the branch protection the first one depends on is not available on this repository at all.
Finding MCG-F-0002 Published 2026-09-05 Confidence: provisional Joseph Walker
This is a provisional Finding. The result held under the stated method and scope, and has not yet been repeated or tested against a wider sample.
Phase 0 gave Masters a publication contract: research records live as Markdown with TOML front matter in Git, a deterministic compiler renders them, and CI decides whether they are valid. That works, and it does not need an editor. But it does require opening a text file, and a record that is inconvenient to revise gets revised less often than it should.
So the question for this review is narrow, and deliberately not "which CMS is better":
What is the smallest, safest way to author and revise Masters publications from a browser while Git, CI and the compiler remain the publication authority?
What both candidates got right
The expectation going in was that the Masters contract would strain at least one of them. It has TOML front matter rather than the near-universal YAML, four publication kinds with different required fields, enumerated truth boundaries, a nested bibliography, and references into a separate evidence corpus.
Neither strained.
Both support toml-frontmatter with +++ delimiters as a first-class format, parsing and re-emitting it rather than converting to YAML. Both express a repeatable group of typed subfields, which is what the bibliography needs. Both express enumerated selects, booleans, date-only fields, plain string lists and a Markdown body.
The decisive test was mechanical rather than declarative. Each project's own serialization code was run over three fixture records — a Finding exercising every canonical field, an Essay and a Specimen — and the output was re-parsed with the production compiler and compared field by field against the original.
Both round-tripped all three kinds with no semantic loss. Values, arrays, booleans, dates, the nested bibliography and the Markdown body all survived, and the Essay and Specimen came back still carrying no confidence level — the truth boundary the schema exists to hold.
Schema fidelity therefore does not decide this, which is a useful negative result in itself: it means the decision has to be made on something else.
There turned out to be two something-elses, and the candidates split them.
Axis one: workflow authority
The required chain has not changed since Phase 0:
browser editor → branch → pull request → compiler → CI → explicit merge → gated deploy
Decap CMS produces that chain itself. With publish_mode: editorial_workflow, saving a draft commits to a branch named cms/<collection>/<slug> and opens a pull request; each subsequent save adds a commit to it. Review is where the tool puts it, not where the author remembers to put it.
Pages CMS 2.1.8 cannot. Reading its source at that version, every content write is a direct commit — octokit.rest.repos.createOrUpdateFileContents against the branch named in the URL — and there is no pull-request API call anywhere in the codebase. It can create a branch, and it can be pointed at one, so the chain is achievable: edit on findings/draft, then open the pull request by hand in GitHub. But nothing in the tool prevents pointing it at main instead, and a workflow that depends on remembering a manual step is a workflow that will eventually be skipped on the day it matters.
On this axis it is not a feature comparison; it is a question of where the review step lives. Decap wins it outright.
The first version of this record stopped here, and concluded. That was the mistake.
Axis two: credential authority
An editor does not only decide how an edit travels. It also decides what capability has to exist in the world for it to travel at all — and that is a separate question with a different answer.
Decap authenticates through a GitHub OAuth application. Its GitHub backend resolves the requested scope as authScope || (openAuthoring ? 'public_repo' : 'repo'), and its configuration schema constrains auth_scope to exactly repo or public_repo. Since public_repo cannot reach a private repository at all, the only working scope is repo, and there is no narrower setting to choose.
GitHub defines repo as full access to public and private repositories, including read and write access to code — and it is scoped to the authorizing person, not to a repository. For the identity that would authorize this editor, that capability extends across every repository the identity can reach, including private repositories. The thing being edited is one directory in one repository.
It is worth separating two credentials that the first version of this record ran together:
- the OAuth application client secret, which stays on the server, is used only for the authorization-code exchange, and never reaches a browser;
- the OAuth access token, which is a bearer capability held in the browser session, carries
reposcope, and is not repository-specific.
Only the first was described. The second is the one that matters.
Pages CMS is architecturally better here, and this is the advantage the original comparison missed. It authenticates as a GitHub App, minting short-lived installation access tokens, and a GitHub App installation can be limited to selected repositories. Its credential's reach is a property of the installation rather than of the human. That does not make it the better editor overall — it still cannot open a pull request — but it does mean the two candidates win different things, and a review that weighs only the axis its preferred candidate wins is not a review.
The boundary that was supposed to contain this
The obvious answer to a broad token is to make the destination unbreakable: protect main, require the validation check, require a pull request. Then it does not matter what the token could theoretically do, because GitHub refuses the merge.
That answer was asserted without being checked. Queried directly, this repository cannot do it:
GET /repos/.../branches/main/protection → 403
GET /repos/.../rulesets → 403
"Upgrade to GitHub Pro or make this repository public to enable this feature."
It is a private repository on a personal account, and GitHub documents protected branches as available in public repositories on the free plan, and in private repositories only on a paid one. The boundary the recommendation depended on is not merely unconfigured. It is unavailable.
The condition is also stronger than "protection exists". A rule an administrator may bypass is not a boundary for a token acting as that administrator, and on a personal repository the authorizing identity is the owner. So authorizing a live pilot requires all four of: main protected; the validation check required; a pull request required; and the editor's principal unable to bypass any of them. None of the four is true today.
There is one safeguard that does already exist, and it is worth stating because it is the reason none of this is urgent. Masters' static deploy is workflow_dispatch only and dry-runs by default, so merging to main publishes nothing by itself. An editor sits two deliberate steps from the public site. That protects the website. It does not constrain the OAuth token to this repository.
Draft is not a branch
A browser editor makes it easy to confuse two dimensions that must stay separate, so it is worth stating plainly.
status is a property of the record. A draft is not a publication: it never renders, never enters the sitemap or the feed, and never enters the Ask Masters corpus — on any branch, main included. Merging a draft is safe and expected; it is how work in progress is preserved rather than lost.
A branch or an open pull request is a property of the record's acceptance into the institutional record, which is a different question. A record marked published on an unmerged branch is not public. A record marked published and merged to main is still not public until the manual deploy runs.
Publication is the conjunction of three separate acts. No editor should be able to make it look like one.
What it costs to be wrong
The portability question — if this editor disappeared tomorrow, what is lost? — was tested rather than asserted.
Canonical records stay Markdown with TOML front matter in Git. Neither candidate adds a field, a sidecar file or proprietary metadata to them; the round-tripped fixtures parse to objects identical to the originals. All editor-specific configuration is a single generated file outside the content tree, and the compiler, CI, sitemap, feed, search index and release build never read it.
Delete the editor configuration and nothing else changes. What is lost is a browser form.
One measurable cost is worth recording. Because an editor rewrites the whole file on save, the first editor save of a hand-written record produces a formatting diff. Over the 73-line fixture, Decap changed 26 lines — it collapses blank-line grouping — and Pages CMS changed 83, re-quoting every scalar and re-wrapping every array. Neither changes a published byte, because the compiler renders from the parsed record rather than the source file's layout. But Decap's output stays closer to what a person would have written, which keeps review diffs legible, and legible diffs are the mechanism by which the pull request is worth anything.
What would have to be true first
The containment problem has several possible answers, and this review defines them rather than choosing one, because choosing is a decision with costs that belong to the institution rather than to a spike.
Upgrading the GitHub plan would make the branch protection real, but it does nothing about the user-wide OAuth token's repository reach, and the owner can bypass protection unless bypass is explicitly disabled. A dedicated identity with access to this repository alone would make repo scope practically repository-shaped — the most direct answer to the axis this correction exposed — though whether that is acceptable depends on GitHub's account terms, which is a question this review notes rather than settles. A repository-scoped GitHub App with a custom Decap adapter is the correct permission model and the most engineering. Making the repository public would enable protection for free and reduce the required scope, but it is a content decision about an evidence corpus and internal documentation, not an authentication workaround.
And there is the option of doing nothing, which already works: hand-authored records in Git, reviewed as they are now, with no new credential in the world at all.
The question for the next decision is exactly one thing: which containment condition will be true before a live pilot is authorized?
What the editor is not allowed to become
A configured form is a second, weaker description of what a Masters publication is. The day it drifts from the schema, the editor starts teaching an author a contract the compiler does not share — offering a confidence level on an essay, say, and failing only much later at the gate.
So neither configuration in this spike is hand-written. Both are generated from the canonical schema, and CI fails if a committed configuration no longer matches what the schema would produce. The invariant is narrow:
An editor may fail to expose a valid advanced field. It may never redefine what constitutes a valid Masters publication.
Stated precisely: generation removes hand-maintained drift between the declaration and the configuration files, and schema-linked tests detect the contract drift that matters — canonical field names, canonical enum values, required fields presented as required, and the absence of a confidence control on the kinds that may not carry one. It does not prove that every editor semantic is derived from every validation rule; control types, field ordering and hints are still written by hand, and the conditional rules are in no form at all. The compiler stays authoritative. A form can under-offer, which produces a record that fails at the gate; it cannot make an invalid record valid.
What this review is not
No live editing session was performed against either editor. Doing so requires a GitHub OAuth application for Decap or a GitHub App installation for Pages CMS, and this spike deliberately provisioned no credentials.
Everything above about how an edit reaches the repository is read from source or from current documentation. Everything about how either product feels to write in — preview behaviour, error messages, long-form comfort, whether it is usable on a phone at all — is untested, and this record is not evidence about any of it. That gap is the reason the conclusion is a bounded pilot rather than an adoption.
Conclusion
Functional preference: Decap CMS 3.16.0. It represents the contract faithfully, it produces the review chain as a property of the tool rather than a habit of the author, it runs as a static bundle plus a stateless authentication handler small enough to live on hosting Masters already pays for, and it can be deleted without touching a single published record.
Authorization to run a live pilot: blocked. Not on anything about Decap's quality, but on two facts about the environment it would run in: the only OAuth scope it can request reaches every repository its authorizing identity can reach, and the branch protection that was supposed to contain that is unavailable on this repository. A hidden merge button is not a boundary.
Pages CMS is not the answer either. It would make publication authority a matter of discipline, which for a single author who is also the reviewer is the wrong trade — but its GitHub App architecture is genuinely better on the credential axis, and saying otherwise would be reporting the comparison Masters wanted rather than the one it got.
The most useful thing this review produced is not the preference. It is the question the preference is waiting on: which containment condition will be true before a browser holds a token on Masters' behalf? Until that is answered, the hand-authored workflow already in place remains the correct way to publish, and the editor stays what it was always meant to be — the least important component in the chain, and the one Masters can decline to switch on.
Research question
What is the smallest, safest way to author and revise Masters publications from a browser while Git, CI and the deterministic compiler remain the publication authority — and what must be true about credentials and branch authority before such an editor may be switched on?
Scope
Two open-source Git-native editors — Decap CMS 3.16.0 and Pages CMS 2.1.8 — assessed against the existing Masters Findings contract on two axes: workflow authority and credential authority. Configuration, authentication and write paths were built and read at pinned versions; each project's own serializer was executed over three real fixture records; the target repository's branch-protection capability was queried directly. No live editing session was performed against either editor, because that requires credentials this review deliberately did not provision. Editors requiring a site framework migration were out of scope.
Systems evaluated
Method
Result
Both candidates represent the contract faithfully and round-trip every publication kind with no semantic loss, so schema fidelity decides nothing. Two other axes do, and the candidates split them. On workflow authority Decap CMS wins outright: it opens a pull request for every saved draft, while Pages CMS 2.1.8 contains no pull-request code path at all. On credential authority Pages CMS wins: it authenticates as a GitHub App and can hold an installation token scoped to selected repositories, whereas Decap's GitHub backend can request nothing narrower than OAuth 'repo' scope, which GitHub defines as read and write across every repository the authorizing user can reach, including private repositories. The boundary that was supposed to contain that breadth is unavailable: branch protection and rulesets both return 403 on this private personal-account repository. The conclusion is therefore split. Functional preference: Decap. Authorization to run a live pilot: blocked, until a credential and branch-authority containment design is demonstrated.
Negative results
Limitations
What would change this conclusion
Sources
The material cited in support of this record, with the date each was read. This is a bibliography, not a claim of exhaustive coverage: a reader should treat any statement here that no listed source supports as unverified. A link that has since changed does not change what was observed on that date; it changes whether the observation still holds.
- decap-cms-backend-github — AuthenticationPage.js Accessed 2026-09-05
Version 3.8.0 resolves the requested OAuth scope as authScope || (openAuthoring ? 'public_repo' : 'repo'), so the private-repository path requests 'repo'.
- GitHub — scopes for OAuth apps Accessed 2026-09-05
Defines 'repo' as full access to public and private repositories, including read and write access to code, statuses, invitations, collaborators, deployment statuses and webhooks.
- GitHub — about protected branches Accessed 2026-09-05
Protected branches are available in public repositories on Free, and in public and private repositories on Pro, Team, Enterprise Cloud and Enterprise Server.
- GitHub REST — branch protection Accessed 2026-09-05
The endpoint queried against this repository, which answered 403 with an upgrade prompt.
- GitHub — authenticating as a GitHub App installation Accessed 2026-09-05
Installation access tokens are short-lived and scoped to the repositories the app is installed on — the model Pages CMS uses and Decap's OAuth path has no equivalent for.
- GitHub — Terms of Service, account requirements Accessed 2026-09-05
Consulted because a dedicated single-repository identity is one containment candidate; the account terms govern whether it is acceptable.
- Decap CMS — configuration options Accessed 2026-09-05
Collection options including format, filter, publish, delete and identifier_field; the toml-frontmatter format with +++ delimiters.
- Decap CMS — editorial workflows Accessed 2026-09-05
Save draft commits to cms/<collection>/<slug> and opens a pull request; approve and publish merges it.
- Decap CMS — GitHub backend Accessed 2026-09-05
All users must have push access to the content repository; GitHub requires a server for authentication.
- Decap CMS — backends overview Accessed 2026-09-05
base_url and the /auth and /callback contract an OAuth proxy must implement.
- Decap CMS — external OAuth clients Accessed 2026-09-05
Community OAuth handlers, including PHP implementations relevant to Masters' existing shared hosting.
- Decap CMS — list widget Accessed 2026-09-05
Repeatable groups via nested fields — the control the structured bibliography needs.
- Decap CMS — datetime widget Accessed 2026-09-05
date_format and picker_utc, required to store a bare ISO calendar date rather than a timestamp.
- decaporg/decap-cms Accessed 2026-09-05
MIT. Source of the format modules used for the round trip; active during the review window.
- Pages CMS Accessed 2026-09-05
MIT, version 2.1.8. Canonical repository; resolves to hunvreus/pagescms. Read for the write path, configuration schema and stored data.
- Pages CMS — hosted editor Accessed 2026-09-05
The hosted operating model offered as the default way to start.
- Pages CMS documentation Accessed 2026-09-05
Configuration reference and the environment the self-hosted application expects.
Related records
Masters claims and sources are institutional evidence objects held once and referenced here by their stable identifiers. Correcting one of them corrects every record that cites it. They are distinct from the bibliography above, which is the material consulted for this publication.