-
Notifications
You must be signed in to change notification settings - Fork 0
feat: content model, state machine, admin UI, and test infrastructure #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
15e2b12
538e3c7
5b6f135
12155e9
9241843
4fe6fa9
3645920
dbcb406
188f17a
d649fe0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,57 @@ | ||||||
| # Changelog | ||||||
|
|
||||||
| All notable changes to git-cms are documented in this file. | ||||||
|
|
||||||
| ## [Unreleased] — git-stunts branch | ||||||
|
|
||||||
| ### Added | ||||||
|
|
||||||
| - **Content Identity Policy (M1.1):** Canonical slug validation with NFKC normalization, reserved word rejection, and `CmsValidationError` contract (`ContentIdentityPolicy.js`) | ||||||
| - **State Machine (M1.2):** Explicit draft/published/unpublished/reverted states with enforced transition rules (`ContentStatePolicy.js`) | ||||||
| - **Admin UI overhaul:** Split/edit/preview markdown editor (via `marked`), autosave, toast notifications, skeleton loading, drag-and-drop file uploads, metadata trailer editor, keyboard shortcuts (`Cmd+S`, `Esc`), dark mode token system | ||||||
| - **DI seam in CmsService:** Optional `graph` constructor param enables `InMemoryGraphAdapter` injection for zero-subprocess tests | ||||||
| - **In-memory test adapter:** Unit tests run in ~11ms instead of hundreds of ms (no `git init`/subprocess forks) | ||||||
| - **E2E test separation:** Real-git smoke tests in `test/git-e2e.test.js`, excluded from default `test:local` runs | ||||||
| - **`test:git-e2e` script:** Run real-git integration tests independently | ||||||
| - **`@git-stunts/alfred` dependency:** Resilience policy library (wired but not yet integrated) | ||||||
| - **`@git-stunts/docker-guard` dependency:** Docker isolation helpers | ||||||
| - **ROADMAP.md:** M0–M6 milestone plan with blocking graph | ||||||
| - **Formal LaTeX ADR** (`docs/adr-tex-2/`) | ||||||
| - **Onboarding scripts:** `setup.sh`, `demo.sh`, `quickstart.sh` with interactive menus | ||||||
| - **Dependency integrity check:** `check-dependency-integrity.mjs` prevents `file:` path regressions | ||||||
|
|
||||||
| ### Changed | ||||||
|
|
||||||
| - CmsService now uses `@git-stunts/git-warp` `GitGraphAdapter` and `@git-stunts/plumbing` `GitRepositoryService` instead of raw plumbing calls | ||||||
| - All `repo.updateRef()` calls routed through `CmsService._updateRef()` for DI/production dual-path | ||||||
| - `listArticles()` supports both plumbing (`for-each-ref`) and in-memory (`graph.listRefs`) paths | ||||||
| - Server endpoints return structured `{ code, field }` errors for validation failures | ||||||
| - Swapped all `file:` dependency paths to versioned npm ranges (PP3) | ||||||
|
|
||||||
| ### Fixed | ||||||
|
|
||||||
| - Symlink traversal hardening in static file serving | ||||||
| - Slug canonicalization enforced at all API ingress points | ||||||
| - Admin UI API calls aligned with server contract (query params, response shapes) | ||||||
| - Server integration test environment stabilized for CI | ||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| - **(P1) Stored XSS via markdown preview:** Sanitize `marked.parse()` output with DOMPurify | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "markdown" is a proper noun — capitalize it.
-- **(P1) Stored XSS via markdown preview:** Sanitize `marked.parse()` output with DOMPurify
+- **(P1) Stored XSS via Markdown preview:** Sanitize `marked.parse()` output with DOMPurify📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[uncategorized] ~34-~34: Did you mean the formatting language “Markdown” (= proper noun)? (MARKDOWN_NNP) 🤖 Prompt for AI Agents |
||||||
| - **(P1) Unpublish atomicity:** Reorder `unpublishArticle` so draft ref updates before published ref deletion | ||||||
| - **(P2) XSS via slug/badge rendering:** Use `textContent` and DOM APIs instead of `innerHTML` interpolation | ||||||
| - **(P2) SRI hashes:** Add `integrity` + `crossorigin` to marked and DOMPurify CDN script tags | ||||||
| - **(P2) Null guards:** `revertArticle` and `unpublishArticle` throw `no_draft` when draft ref is missing; `_resolveArticleState` throws `article_not_found` when both draft and published refs are missing | ||||||
| - **(P2) uploadAsset DI guard:** Throw `unsupported_in_di_mode` when `cas`/`vault` are null | ||||||
| - **(P2) Trailer key casing:** Use camelCase `updatedAt` in `unpublishArticle` and `revertArticle` (was lowercase `updatedat` which broke `renderBadges` lookups); destructure out decoded lowercase key before spreading to avoid `TrailerInvalidError` | ||||||
| - **(P2) XSS in `escAttr`:** Escape single quotes (`'` → `'`) to prevent injection into single-quoted attributes | ||||||
| - **(P2) Supply-chain hardening:** Vendor Open Props CSS files locally (`public/css/`) instead of `@import` from unpkg, eliminating CDN dependency and SRI gap | ||||||
| - **(P2) Monkey-patch safety:** E2E test restores `plumbing.execute` in `finally` block | ||||||
| - Unknown `draftStatus` in `resolveEffectiveState` now throws `unknown_status` instead of silently falling through to draft | ||||||
| - Removed double-canonicalization in `_resolveArticleState` | ||||||
| - Replaced sequential `readRef` loop with `Promise.all` in `listArticles` DI path | ||||||
| - Admin UI: fixed `removeTrailerRow` redundant positional removal, FileReader error handling, autosave-while-saving guard, Escape key scoped to editor panel, drag-and-drop scoped to drop zone | ||||||
| - Test cleanup: extracted `createTestCms()` helper, converted try/catch assertions to `.rejects.toMatchObject()`, added guard-path tests | ||||||
| - `TRANSITIONS` Sets now `Object.freeze`d to prevent mutation via `.add()`/`.delete()` | ||||||
| - DI-mode `_updateRef` now performs manual CAS check against `oldSha` | ||||||
| - Server tests assert setup call status codes to surface silent failures | ||||||
| - Vitest exclude glob `test/git-e2e*` → `test/git-e2e**` to cover future subdirectories | ||||||
|
|
||||||
| [Unreleased]: https://github.com/flyingrobots/git-cms/compare/main...git-stunts | ||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Markdownlint MD022: headings need surrounding blank lines.
Lines 7, 22, and 29 (
### Added,### Changed,### Fixed) each lack a blank line before them. This will triggerMD022in CI if markdownlint is enforced.Proposed fix (showing line 5–9; repeat for 21–22 and 28–29)
## [Unreleased] — git-stunts branch ### Added + - **Content Identity Policy (M1.1):** Canonical slug validation with NFKC normalization, reserved word rejection, and `CmsValidationError` contract (`ContentIdentityPolicy.js`)📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 7-7: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents