domma-cms 1.6.60 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4344 -0
- package/README.md +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/page-editor-reference.js +5 -5
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/templates/docs/usage-shortcodes.html +39 -0
- package/docs/DommaFeatures.md +3005 -0
- package/docs/MultiTenant.md +176 -0
- package/docs/api-reference.md +1667 -0
- package/docs/cache.md +96 -0
- package/docs/configuration.md +1811 -0
- package/docs/editions-and-licences.md +190 -0
- package/docs/getting-started.md +201 -0
- package/docs/guides/form-triggers.md +260 -0
- package/docs/guides/mongodb-storage-adapter.md +282 -0
- package/docs/guides/multi-page-forms.md +121 -0
- package/docs/images/scribe-composer.png +0 -0
- package/docs/markdown-shortcodes.md +1738 -0
- package/docs/pentest-report-2026-04-03.md +181 -0
- package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
- package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
- package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
- package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
- package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
- package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
- package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
- package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
- package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
- package/docs/plans/2026-03-05-user-types-design.md +139 -0
- package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
- package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
- package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
- package/docs/plans/2026-06-10-core-project-design.md +240 -0
- package/docs/plugin-development.md +827 -0
- package/docs/recruitment-recipe.md +501 -0
- package/docs/scaffolding.md +312 -0
- package/docs/screenshots/calculator-floating.png +0 -0
- package/docs/screenshots/calculator-standard.png +0 -0
- package/docs/screenshots/email-settings.png +0 -0
- package/docs/screenshots/messages-count.png +0 -0
- package/docs/structure/user-types.md +107 -0
- package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
- package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
- package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
- package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
- package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
- package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
- package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
- package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
- package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
- package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
- package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
- package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
- package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
- package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
- package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
- package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
- package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
- package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
- package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
- package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
- package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
- package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
- package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
- package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
- package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
- package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
- package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
- package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
- package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
- package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
- package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
- package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
- package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
- package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
- package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
- package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
- package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
- package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
- package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
- package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
- package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
- package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
- package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
- package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
- package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
- package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
- package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
- package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
- package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
- package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
- package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
- package/docs/theming.md +320 -0
- package/package.json +2 -2
- package/public/js/celebrations/core/canvas.js +1 -1
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
- package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
- package/public/js/celebrations/message-attrs.mjs +1 -0
- package/public/js/celebrations/themes/anniversary.js +1 -0
- package/public/js/celebrations/themes/autumn.js +1 -0
- package/public/js/celebrations/themes/birthday.js +1 -0
- package/public/js/celebrations/themes/butterfly-flight.js +1 -0
- package/public/js/celebrations/themes/calendar.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year.js +1 -0
- package/public/js/celebrations/themes/christmas.js +1 -1
- package/public/js/celebrations/themes/easter.js +1 -0
- package/public/js/celebrations/themes/garden-drawing.js +1 -0
- package/public/js/celebrations/themes/garden.js +1 -0
- package/public/js/celebrations/themes/graduation.js +1 -0
- package/public/js/celebrations/themes/greeting.js +1 -0
- package/public/js/celebrations/themes/make-it-rain.js +1 -0
- package/public/js/celebrations/themes/money-rain.js +1 -0
- package/public/js/celebrations/themes/new-year.js +1 -0
- package/public/js/celebrations/themes/occasion-drawing.js +1 -0
- package/public/js/celebrations/themes/occasions.js +1 -0
- package/public/js/celebrations/themes/rain-control.js +1 -0
- package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
- package/public/js/celebrations/themes/sparkler-font.js +1 -0
- package/public/js/celebrations/themes/sparkler-writing.js +1 -0
- package/public/js/celebrations/themes/summer-garden.js +1 -0
- package/public/js/celebrations/themes/winter.js +1 -0
- package/public/js/celebrations/trait-data.mjs +1 -1
- package/public/js/effects.js +1 -1
- package/scripts/build.js +4 -0
- package/server/services/markdown.js +5 -2
- package/server/templates/page.html +1 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Core Project - Design Document
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-10
|
|
4
|
+
**Status:** Implemented (2026-06-10)
|
|
5
|
+
**Approach:** Core by exclusion - a seeded, protected `core` project that owns every artefact not claimed by another project. No stamping, no data migration.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Every site gains a built-in **Core** project representing the core site itself. All
|
|
12
|
+
artefacts that are not attached to another project belong to Core - by **resolution
|
|
13
|
+
fallback**, not by tagging. The Core project record is seeded at boot (create-if-absent),
|
|
14
|
+
cannot be deleted, and its slug/rootUrl are locked.
|
|
15
|
+
|
|
16
|
+
This completes the Projects model: "untagged" stops being a half-state that means both
|
|
17
|
+
"site-wide" and "the main site". Instead, the main site has a real identity - a sidebar
|
|
18
|
+
entry, an overview page with artefact counts, and per-type filtered lists - exactly like
|
|
19
|
+
any scaffolded project.
|
|
20
|
+
|
|
21
|
+
### Why fallback, not stamping
|
|
22
|
+
|
|
23
|
+
The obvious implementation (write `meta.project: 'core'` onto every existing artefact and
|
|
24
|
+
every initial artefact at site generation) was considered and rejected:
|
|
25
|
+
|
|
26
|
+
1. **Access-control regression.** `canSeeArtefact()` treats untagged artefacts as visible
|
|
27
|
+
to *everyone*, including scoped users. A recipe-seeded user with
|
|
28
|
+
`projects: ['onboarding']` currently sees the navbar menu, the site pages, and shared
|
|
29
|
+
blocks because they are untagged. Stamping them `core` would hide all of it from every
|
|
30
|
+
scoped user on every site, on upgrade day.
|
|
31
|
+
2. **Migration burden.** Stamping requires rewriting page frontmatter, menu/form/block/
|
|
32
|
+
collection meta, and a guarded one-shot migration per site - and only fixes the past.
|
|
33
|
+
Any artefact created later without a tag (plugins, API clients, hand-edited files)
|
|
34
|
+
becomes a stray again.
|
|
35
|
+
3. **Fallback fixes the invariant.** "Not in another project ⇒ in Core" holds forever,
|
|
36
|
+
for past and future artefacts, with zero data rewrites. The retrofit to existing sites
|
|
37
|
+
is simply the CMS update itself.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Data Model
|
|
42
|
+
|
|
43
|
+
### Seeded record
|
|
44
|
+
|
|
45
|
+
`content/collections/projects/data.json` gains one seeded entry:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"slug": "core",
|
|
50
|
+
"name": "Core",
|
|
51
|
+
"description": "The core site - everything not assigned to another project.",
|
|
52
|
+
"icon": "home",
|
|
53
|
+
"rootUrl": "/",
|
|
54
|
+
"sortOrder": -1,
|
|
55
|
+
"protected": true
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- **Locked fields:** `slug` (already immutable), `rootUrl`, `protected`.
|
|
60
|
+
- **Editable fields:** `name`, `description`, `icon`, `sortOrder`.
|
|
61
|
+
- `sortOrder: -1` pins Core first in the sidebar by default; admins may re-order.
|
|
62
|
+
- `rootUrl: '/'` is **descriptive only** - resolution does not depend on it (see below).
|
|
63
|
+
`validateProject()` currently rejects `rootUrl === '/'`; the rule is relaxed to allow
|
|
64
|
+
it for the `core` slug only.
|
|
65
|
+
|
|
66
|
+
### Constant + display flag
|
|
67
|
+
|
|
68
|
+
Enforcement follows the `BASE_ROLE_NAMES` precedent in `roles.js`:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
// server/services/projects.js
|
|
72
|
+
export const CORE_PROJECT_SLUG = 'core';
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The constant is the enforcement mechanism (guards compare against it); the
|
|
76
|
+
`protected: true` field on the record is a display hint so the admin UI never hardcodes
|
|
77
|
+
the slug. The projects preset schema in `presetCollections.js` gains a
|
|
78
|
+
`protected` (boolean, default `false`) field.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Seeding
|
|
83
|
+
|
|
84
|
+
A new `seedCoreProject()` in `server/services/projects.js`, called from `server.js`
|
|
85
|
+
immediately after `presetCollections.seedAll()`:
|
|
86
|
+
|
|
87
|
+
1. List projects; if any entry has `slug === 'core'` → **adopt**: ensure
|
|
88
|
+
`protected: true` is set, leave all other fields untouched (a pre-existing user-created
|
|
89
|
+
"core" project keeps its name/icon/rootUrl - resolution does not depend on them).
|
|
90
|
+
2. Otherwise → create the seeded record above.
|
|
91
|
+
|
|
92
|
+
Idempotent and record-level (checks for the slug, **not** for `data.json` existence -
|
|
93
|
+
`seedAll()` already creates an empty `data.json`, and retrofitted sites have existing
|
|
94
|
+
project entries that must be preserved).
|
|
95
|
+
|
|
96
|
+
This single hook is the entire retrofit: every existing site gets Core on first boot
|
|
97
|
+
after upgrading. No per-site scripts, no `.bak` files. Fleet sites that predate Projects
|
|
98
|
+
entirely (e.g. older deployments) retrofit by upgrading to current - same path.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Resolution Changes (`server/services/projects.js`)
|
|
103
|
+
|
|
104
|
+
### Pages - `getProjectForPage(urlPath, explicitProject)`
|
|
105
|
+
|
|
106
|
+
| Rule | Before | After |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `project: null` in frontmatter | `null` (untagged opt-out) | `'core'` - opt-out is meaningless once Core owns the floor; treated as core and deprecated in docs |
|
|
109
|
+
| `project: '<slug>'` | use it | unchanged |
|
|
110
|
+
| Missing field, prefix match | longest `rootUrl` prefix wins | unchanged - scaffolded projects keep winning (their rootUrls are longer than nothing) |
|
|
111
|
+
| Missing field, no match | `null` | `'core'` |
|
|
112
|
+
|
|
113
|
+
Implementation: the matcher loop is untouched; the final return becomes
|
|
114
|
+
`return best ?? CORE_PROJECT_SLUG`. Core deliberately does **not** participate in prefix
|
|
115
|
+
matching - `startsWith(rootUrl + '/')` with `rootUrl: '/'` would test `startsWith('//')`
|
|
116
|
+
and match nothing anyway. Fallback is both simpler and correct.
|
|
117
|
+
|
|
118
|
+
Safety: if the core record is somehow absent (mid-boot, hand-deleted data file), return
|
|
119
|
+
`null` as before; the next boot re-seeds.
|
|
120
|
+
|
|
121
|
+
### Non-page artefacts
|
|
122
|
+
|
|
123
|
+
A small helper, used everywhere a project grouping is displayed or enumerated:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
export function resolveArtefactProject(artefact) {
|
|
127
|
+
return artefact?.meta?.project || CORE_PROJECT_SLUG;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`getArtefactsForProject(slug)` switches its eight `meta.project === slug` comparisons to
|
|
132
|
+
`resolveArtefactProject(x) === slug`. The pages branch already works unchanged (it calls
|
|
133
|
+
`getProjectForPage`, which now falls back to core). Net effect:
|
|
134
|
+
`getArtefactsForProject('core')` enumerates everything unclaimed - the "Core site"
|
|
135
|
+
overview - and other projects' results are byte-identical to today.
|
|
136
|
+
|
|
137
|
+
### Access control - explicitly unchanged
|
|
138
|
+
|
|
139
|
+
`canSeeArtefact()` gains one clause and **no semantic change** for existing data:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const project = artefact?.meta?.project;
|
|
143
|
+
if (!project || project === CORE_PROJECT_SLUG) return true;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Core is an organisational bucket, never an access boundary.** Untagged artefacts and
|
|
147
|
+
explicitly-`core`-tagged artefacts are visible to all authenticated users, exactly as
|
|
148
|
+
untagged is today. Scoped users keep their navbar, site pages, and shared blocks.
|
|
149
|
+
"Core-only" user scoping is out of scope for this design; `projects: []` (no restriction)
|
|
150
|
+
remains the way to express it. If Core ever needs to be restrictable, that is a separate
|
|
151
|
+
design with real consequences (every scoped recipe user would lose the public site).
|
|
152
|
+
|
|
153
|
+
`listProjectsForUser()` always includes Core for any authenticated caller (consistency:
|
|
154
|
+
if `canSeeArtefact` says yes to core artefacts, the project list says yes to the
|
|
155
|
+
project). Consequence: the Projects sidebar section - currently hidden when the list is
|
|
156
|
+
empty - now renders on **every** site, with at least Core in it. This is the desired
|
|
157
|
+
headline behaviour.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Protection (delete / update / untag)
|
|
162
|
+
|
|
163
|
+
Guards at both layers, following the roles precedent (`Cannot remove built-in role`):
|
|
164
|
+
|
|
165
|
+
| Operation | Service (`projects.js`) | Route (`routes/api/projects.js`) |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `DELETE /projects/core` | `deleteProject()` throws `Cannot delete the core project` | 403 **before** the artefact-count enumeration (don't waste the scan; the existing 409-when-tagged guard does not protect core because untag-all is an escape hatch) |
|
|
168
|
+
| `POST /projects/core/untag-all` | `untagAllForProject()` throws | 403 - meaningless for core: resolution re-claims everything instantly |
|
|
169
|
+
| `PUT /projects/core` | `updateProject()` rejects changes to `rootUrl` and `protected`; accepts `name`, `description`, `icon`, `sortOrder` | 400 with field-specific message |
|
|
170
|
+
|
|
171
|
+
Note the interaction with **other** projects' untag-all: artefacts untagged from any
|
|
172
|
+
project now land in Core (previously "untagged"). Same data state, new label - the word
|
|
173
|
+
"untagged" disappears from UI language in favour of "Core".
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Scaffolder & Site Generation
|
|
178
|
+
|
|
179
|
+
**No scaffolder changes.** Recipes with a `project` block keep creating their project and
|
|
180
|
+
stamping `meta.project: '<namespace>'` exactly as today; their longer `rootUrl` prefixes
|
|
181
|
+
keep winning page resolution over the core fallback. Recipe-seeded users
|
|
182
|
+
(`projects: ['<namespace>']`) continue to see core artefacts via the access rule above.
|
|
183
|
+
|
|
184
|
+
**No site-generation changes.** `npx` generation, the Domma CMS Manager, payment-flow
|
|
185
|
+
provisioning, and injection all produce untagged initial artefacts as today - boot
|
|
186
|
+
seeding plus resolution fallback files them under Core automatically. `scripts/setup.js`,
|
|
187
|
+
`scripts/reset.js`, `scripts/fresh.js` are untouched (reset wipes content; the next boot
|
|
188
|
+
re-seeds Core).
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Admin UI (`admin/` - upstream-replaced, ships with the CMS release)
|
|
193
|
+
|
|
194
|
+
| View | Change |
|
|
195
|
+
|---|---|
|
|
196
|
+
| `views/projects.js` (list) | Render a "System" badge on `protected: true` rows; hide the delete button |
|
|
197
|
+
| `views/project-settings.js` | When `protected`: disable the Root URL input, hide the danger zone (untag-all + delete) entirely |
|
|
198
|
+
| `views/project-detail.js` | No change - counts come from `GET /projects/:slug/artefacts`, which now resolves correctly for core |
|
|
199
|
+
| Sidebar Projects section | No code change needed; it now always renders because the list is never empty |
|
|
200
|
+
| Quick-create under Core | Omit the `meta.project` stamp (artefacts stay in the natural default state, which *is* Core); an explicit `'core'` tag is treated identically if one is ever written |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Tests
|
|
205
|
+
|
|
206
|
+
- **Seeding:** creates core when absent; idempotent on re-boot; adopts a pre-existing
|
|
207
|
+
`core` slug without clobbering fields; survives alongside existing project entries.
|
|
208
|
+
- **Resolution:** `getProjectForPage('/about', undefined)` → `'core'`;
|
|
209
|
+
`project: null` → `'core'`; longest-prefix project still beats core;
|
|
210
|
+
`resolveArtefactProject({})` → `'core'`.
|
|
211
|
+
- **Enumeration:** `getArtefactsForProject('core')` counts untagged artefacts of all nine
|
|
212
|
+
types; other projects' counts unchanged (regression guard).
|
|
213
|
+
- **Access:** extend `tests/services/projects-scope.test.js` - scoped user sees
|
|
214
|
+
core-resolved and explicitly-core-tagged artefacts; `listProjectsForUser` includes core
|
|
215
|
+
for scoped users.
|
|
216
|
+
- **Protection:** service throws on delete/untag-all of core; routes return 403; update
|
|
217
|
+
rejects `rootUrl`/`protected` changes but accepts `name`/`icon`.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Task Breakdown
|
|
222
|
+
|
|
223
|
+
| # | Task | Files | Size |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| 1 | `CORE_PROJECT_SLUG`, `resolveArtefactProject()`, `seedCoreProject()`, fallback in `getProjectForPage`, resolution scan in `getArtefactsForProject`, guards in `deleteProject`/`updateProject`/`untagAllForProject`, `canSeeArtefact` clause, `listProjectsForUser` inclusion, relax `validateProject` for core's `/` | `server/services/projects.js` | ~50 lines |
|
|
226
|
+
| 2 | `protected` field on projects preset schema | `server/services/presetCollections.js` | ~2 lines |
|
|
227
|
+
| 3 | Call `seedCoreProject()` at boot after `seedAll()` | `server/server.js` | ~3 lines |
|
|
228
|
+
| 4 | 403 guards on DELETE and untag-all; PUT field rejection passthrough | `server/routes/api/projects.js` | ~15 lines |
|
|
229
|
+
| 5 | System badge, hidden delete, locked settings fields | `admin/js/views/projects.js`, `project-settings.js` | ~25 lines |
|
|
230
|
+
| 6 | Tests per section above | `tests/services/`, `tests/routes/` | ~150 lines |
|
|
231
|
+
| 7 | Docs: CLAUDE.md Projects section, `docs/configuration.md`, `docs/scaffolding.md` - document core-by-exclusion, deprecate `project: null` | docs | - |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Out of Scope / Future
|
|
236
|
+
|
|
237
|
+
- **Core as an access boundary** - deliberately excluded; see Access control section.
|
|
238
|
+
- **Per-service `untagAllForProject` plumbing for pages/roles/users** - pre-existing
|
|
239
|
+
deferral, unaffected by this design.
|
|
240
|
+
- **Renaming "untagged" UI strings** beyond the projects views - sweep opportunistically.
|