docs: move golem15.user to sm-user-plugin and supersede the Phase 1 extraction deferral
- new note .planning/notes/core-plugins-own-repos.md: shared core plugins live in sm-<name>-plugin repos mounted as submodules - 01-CONTEXT deferral points to the note; PROJECT constraint and Key Decisions row - ROADMAP Phase 12 repos and the 12-01 entry, and Phase 12 plans 12-01, 12-02, 12-05 name sm-user-plugin and the submodule commit workflow
This commit is contained in:
@@ -134,7 +134,7 @@ Quality
|
||||
- **Compatibility**: `vue-fonoteka-app` and `fonoteka-mcp` must run unchanged; the Nuxt app's requests define the contract and response shapes are not improved during the port
|
||||
- **Realtime**: keep the Centrifugo server and port only the publisher and token issuing — the Nuxt client connects to Centrifugo directly
|
||||
- **Plugins**: compiled at build time; no runtime plugin loading without a decision note
|
||||
- **Two repositories**: `summercms.go` is the framework only (the `summer` packages, the CLI, the admin SPA shell, the parity harness tooling) and knows nothing about Płytarium; `fonoteka.go`, a sibling directory in the meta repo, is the application: a go.work workspace holding the ported plugins (user, translate, feedback, sitemap, fonoteka) and the app binary; websockets is not a separate app plugin (D-16, Phase 11): the framework realtime package `lighthouse` plus fonoteka's `config/realtime.yaml`, channel authorizers, `lighthouse.Mount` call and `ws-api` bucket replace it, requiring the framework by module path with a local replace during development. Roadmap phases name which repo each plan writes to; planning docs stay in `summercms.go/.planning`
|
||||
- **Two repositories**: `summercms.go` is the framework only (the `summer` packages, the CLI, the admin SPA shell, the parity harness tooling) and knows nothing about Płytarium; `fonoteka.go`, a sibling directory in the meta repo, is the application: a go.work workspace holding the application plugins (fonoteka, translate, feedback, sitemap) and the app binary, and mounting shared core plugins from their own `sm-<name>-plugin` repos as git submodules (the user plugin is `sm-user-plugin`, module `git.golem15.com/golem15/sm-user-plugin`, at `plugins/golem15/user`; see `.planning/notes/core-plugins-own-repos.md`); websockets is not a separate app plugin (D-16, Phase 11): the framework realtime package `lighthouse` plus fonoteka's `config/realtime.yaml`, channel authorizers, `lighthouse.Mount` call and `ws-api` bucket replace it, requiring the framework by module path with a local replace during development. Roadmap phases name which repo each plan writes to; planning docs stay in `summercms.go/.planning`
|
||||
- **Workflow**: lean planning (few, large plans per phase), a plan-count checkpoint before PLAN.md files are written, unit tests as the last plan of every phase, `go vet` and `go test ./...` green at every commit
|
||||
- **Commits**: no co-author tags; one logical change per commit; planning docs and code in separate commits
|
||||
- **Core plugin contracts**: the PHP user, blog, pages and payment plugins are shared across many projects; the Go ports must preserve their contracts and the PHP originals are not changed as part of this project
|
||||
@@ -161,6 +161,7 @@ Quality
|
||||
| Parity harness is a day-one workstream | Fixture recording needs only the running PHP backend; it is the acceptance mechanism for every port phase | ✓ Good (Phase 2, 2026-09-17: 154/154 PHP self-replay, Nuxt/MCP fixtures, testcontainers Go runner) |
|
||||
| WinterCMS websockets plugin dissolved into the framework `lighthouse` package (Phase 11 D-16) | Realtime is transport-neutral framework code (drivers, authorizer registry, broadcasts); the app only binds config, the collection/wishlist authorizers, the Mount call, the ws-api bucket and the Album binding, so a separate app plugin would be an empty shell | ✓ Good (Phase 11 plan 11-03) |
|
||||
| Framework and application in two repos from day one (`summercms.go`, `fonoteka.go`) | Retrofitting the split later is more disruptive; shared stack plugins can be extracted for keios.eu without touching the framework; the framework never imports an app | ✓ Good (Phase 3: fonoteka.go workspace, user + fonoteka plugins, app.Handler test seam) |
|
||||
| Shared core plugins live in their own `sm-<name>-plugin` repos, mounted into each application as git submodules (first: sm-user-plugin, module `git.golem15.com/golem15/sm-user-plugin`) | A second application (sm-summercmsio-app) exists and the Journal port is next; one copy per core plugin instead of forks; supersedes the Phase 1 keios.eu trigger (`.planning/notes/core-plugins-own-repos.md`) | ✓ Good (2026-10-02, quick 261002-esz) |
|
||||
|
||||
## Evolution
|
||||
|
||||
|
||||
@@ -603,7 +603,7 @@ Plans:
|
||||
**Goal**: Collections and Albums endpoints are ported with byte-compatible request/response shapes, including active-context switching, editor invitations, ratings, reservations, cover handling and search.
|
||||
**Mode:** mvp
|
||||
**Depends on**: Phase 5, Phase 6, Phase 7, Phase 11
|
||||
**Repos:** fonoteka.go
|
||||
**Repos:** fonoteka.go, sm-user-plugin (submodule at fonoteka.go/plugins/golem15/user)
|
||||
**Requirements**: API-01, API-02
|
||||
**Success Criteria** (what must be TRUE):
|
||||
|
||||
@@ -618,7 +618,7 @@ Plans:
|
||||
Plans:
|
||||
|
||||
**Wave 1**
|
||||
- [ ] 12-01-PLAN.md — Framework gaps (summercms.go): Laravel-semantics request validator with pl/en catalogs, attach URL/webp, tide multipart and upload masks, beachcomber found/weights; user groups in the Go user plugin (D-25); ROADMAP/REQUIREMENTS rewording
|
||||
- [ ] 12-01-PLAN.md — Framework gaps (summercms.go): Laravel-semantics request validator with pl/en catalogs, attach URL/webp, tide multipart and upload masks, beachcomber found/weights; user groups in the Go user plugin sm-user-plugin (D-25); ROADMAP/REQUIREMENTS rewording
|
||||
|
||||
**Wave 2** *(blocked on Wave 1 completion)*
|
||||
- [ ] 12-02-PLAN.md — Active context, collections and share: token-aware resolver, AccessibleBy, provisioning, gates, collection serializer, collections routes, me/context, realtime/channels, collection/share, per-route scopes and per-album delete (D-26)
|
||||
|
||||
39
.planning/notes/core-plugins-own-repos.md
Normal file
39
.planning/notes/core-plugins-own-repos.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Decision: Shared core plugins live in their own sm-*-plugin repos
|
||||
|
||||
**Date:** 2026-10-02
|
||||
**Status:** Accepted
|
||||
**Context:** quick task 261002-esz (extract golem15.user from fonoteka.go)
|
||||
**Supersedes:** the Phase 1 deferred item "Extraction of shared stack plugins into their own repos — only when a second app (keios.eu) needs one" (`.planning/phases/01-framework-kernel-foundation/01-CONTEXT.md`)
|
||||
|
||||
## Decision
|
||||
|
||||
Shared core plugins live in their own repositories, `git.golem15.com/golem15/sm-<name>-plugin`, starting with the user plugin and followed by blog, pages, payment and the other cross-project Golem15 plugins as they are ported.
|
||||
|
||||
- The Go module path equals the repo path: `git.golem15.com/golem15/sm-<name>-plugin`. The Go package and the plugin ID keep their plain names, so the user plugin is still package `user` and plugin `golem15.user`.
|
||||
- Each application mounts them as git submodules at `plugins/<vendor>/<name>`, lists them in `go.work` and adds a `require` plus a local `replace git.golem15.com/golem15/sm-<name>-plugin => ./plugins/<vendor>/<name>` to its `go.mod`, and names the module in `summer.yaml`.
|
||||
- The plugin's own `go.mod` replaces the framework with `../../../../summercms.go`, which resolves when the plugin is mounted at `<app>/plugins/<vendor>/<name>` next to the framework checkout.
|
||||
- Application-specific plugins, such as `golem15.fonoteka`, stay in the application repository.
|
||||
|
||||
## Trigger
|
||||
|
||||
The Phase 1 deferral waited for a second application. One now exists: sm-summercmsio-app, which already mounts its site plugin sm-summercmsio-plugin as a submodule at `plugins/golem15/summercms` (the layout precedent this note generalises). The Journal port is next and needs the same user plugin. Keeping one copy per core plugin avoids forks between applications.
|
||||
|
||||
## How it was done for golem15.user
|
||||
|
||||
- The plugin's history was split out of fonoteka.go with `git subtree split --prefix=plugins/golem15/user` (21 commits, tree byte-identical to the in-tree directory) and pushed as `master` of `git@git.golem15.com:golem15/sm-user-plugin.git`.
|
||||
- fonoteka.go mounts it back as a submodule at the same path, `plugins/golem15/user`, so `go.work` did not change.
|
||||
- The module was renamed to `git.golem15.com/golem15/sm-user-plugin` in the plugin repo, and every importer in fonoteka.go (app, parity, the fonoteka plugin, both `go.mod` files, `summer.yaml`, the regenerated `plugins.gen.go`) followed.
|
||||
- No behaviour change: plugin ID, tables, migration IDs, config keys `golem15.user.*`, routes and payloads are unchanged. The plugin gained a `go mod tidy` for standalone module mode and a README.
|
||||
|
||||
## Workflow
|
||||
|
||||
- Change a submodule plugin inside its checkout (`git -C <app>/plugins/<vendor>/<name>`): commit there and push its `master` first, then commit the bumped pointer in the application repo as a separate commit.
|
||||
- Never stage files inside a submodule from the application repo.
|
||||
- Core plugin contracts (routes, payloads, tables, migration IDs, config keys) stay non-breaking unless the user asks, because several applications mount the same plugin.
|
||||
|
||||
## Consequences and follow-ups
|
||||
|
||||
- A plugin README never names a consuming application; it says "the application" or "host application" and uses neutral examples.
|
||||
- `TestRegisterCORSPath` in the user plugin reads the host application's `config/http.yaml`, so the plugin's full test suite runs only inside an application checkout. Making it self-contained is a follow-up.
|
||||
- Two code comments in the user plugin (`models/organisation.go`, `updates/10_organisations.go`) still name the first consuming application; they were left unchanged in the pure move.
|
||||
- fonoteka.go itself becoming `sm-fonoteka-app` is a separate pending rename.
|
||||
@@ -108,7 +108,7 @@ Phase 1 delivers the framework kernel in `summercms.go` only: config (`compass`)
|
||||
|
||||
- Scaffolding of model, migration, command, job and admin-controller stubs by `make:*` commands — Phase 4 (CLI-02). Phase 1's `make:plugin` produces only the compiling minimum.
|
||||
- Admin config editor consuming `Persist` overrides — later admin phase; Phase 1 only provides the API.
|
||||
- Extraction of shared stack plugins into their own repos — only when a second app (keios.eu) needs one.
|
||||
- Extraction of shared stack plugins into their own repos — only when a second app (keios.eu) needs one. Superseded 2026-10-02 by `.planning/notes/core-plugins-own-repos.md` (golem15.user moved to sm-user-plugin).
|
||||
- One-line corrections to research docs (module path, air, `plugins.<name>` sketch) — do as a docs commit alongside Phase 1 planning, not as code.
|
||||
|
||||
</deferred>
|
||||
|
||||
@@ -135,12 +135,12 @@ ROADMAP Phase 12 goal (verbatim, not in user-story form; MVP precedent of Phases
|
||||
This plan's slice: the framework can produce every Laravel 422 body, record and replay multipart uploads, emit Winter-shaped upload URLs, decode webp, report the search engine's `found` count and rank fields by weight; the user plugin knows user groups; and the roadmap and requirements say what Phase 12 actually ships. Nothing here is user-visible on its own: plans 12-02 to 12-04 build every endpoint on these pieces.
|
||||
|
||||
<objective>
|
||||
Close the framework gaps RESEARCH Finding 5 lists (summercms.go), add user groups to the Go user plugin (fonoteka.go, the user plugin lives at `../fonoteka.go/plugins/golem15/user`), and correct the planning-doc wording.
|
||||
Close the framework gaps RESEARCH Finding 5 lists (summercms.go), add user groups to the Go user plugin (repo sm-user-plugin, module `git.golem15.com/golem15/sm-user-plugin`, mounted as a git submodule at `../fonoteka.go/plugins/golem15/user`), and correct the planning-doc wording.
|
||||
|
||||
Purpose: every Phase 12 endpoint needs Laravel-exact 422 bodies (D-21), multipart recordings (D-11), PHP upload URLs (D-22), webp (D-24), a re-gated search total (D-19) and the site-admin predicate (D-25).
|
||||
Output: `lagoon.ValidateRequest` with pl/en catalogs; attach `URL`/`PublicURL` and webp; tide multipart and upload/publication masks; beachcomber `PageSearcher` and weights; user groups; README and docs updates; reworded ROADMAP/REQUIREMENTS.
|
||||
|
||||
Repos: summercms.go (framework and planning docs) and fonoteka.go (user plugin). Framework code, tests, READMEs and docs use neutral names (acme, blog, posts) and never name the application. Planning docs and code go in separate commits; never add co-author tags.
|
||||
Repos: summercms.go (framework and planning docs), sm-user-plugin (user groups, committed inside the submodule and pushed to its origin master first) and fonoteka.go (parity schema allow-list plus the bumped submodule pointer). Framework code, tests, READMEs and docs use neutral names (acme, blog, posts) and never name the application. Planning docs and code go in separate commits; never add co-author tags.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@@ -268,11 +268,13 @@ Detector run on the Phase 12 ROADMAP section: detected=false. Considered D-25 (u
|
||||
|
||||
(2) Docs in the same change: modules/beachcomber/README.md and docs/services/search.md (PageSearcher, SearchResult, the helper, QueryByWeights, the 250 per-page limit, and that ids remain candidates only that callers re-gate in SQL).
|
||||
|
||||
(3) User groups, per D-25 (fonoteka.go user plugin, additive): models/user_group.go `UserGroup{ID uint; Name string; Code *string; Description *string; Permissions *string; CreatedAt, UpdatedAt *time.Time}` with TableName `user_groups`, registered in the models registry like the other models; add `Groups []UserGroup` to models.User with tag `gorm:"many2many:users_groups;joinForeignKey:user_id;joinReferences:user_group_id"` and `json:"-"` (GORM never writes it unless a caller associates groups; nothing in the user plugin does). Migration file updates/202610020001_create_user_groups.go (ID `202610020001_create_user_groups`): create `user_groups` (id serial primary key, name varchar(255) not null, code varchar(255) null with an index, description text null, permissions text null, created_at and updated_at timestamp null) and `users_groups` (user_id integer not null, user_group_id integer not null, primary key (user_id, user_group_id) named user_group), then insert the Guest/guest and Registered/registered rows with PHP's descriptions; Rollback drops both tables. classes/user_groups.go: `UserGroupCodes(ctx, db, userID uint) ([]string, error)` (codes of the user's groups ordered by user_groups.id, null codes skipped) and `HasGroupCode(ctx, db, userID uint, code string) (bool, error)`. The user API payload keeps `"groups":[]` exactly as today (D-25: contract unchanged); do not read the new table in any user-plugin handler.
|
||||
(3) User groups, per D-25 (sm-user-plugin, additive): models/user_group.go `UserGroup{ID uint; Name string; Code *string; Description *string; Permissions *string; CreatedAt, UpdatedAt *time.Time}` with TableName `user_groups`, registered in the models registry like the other models; add `Groups []UserGroup` to models.User with tag `gorm:"many2many:users_groups;joinForeignKey:user_id;joinReferences:user_group_id"` and `json:"-"` (GORM never writes it unless a caller associates groups; nothing in the user plugin does). Migration file updates/202610020001_create_user_groups.go (ID `202610020001_create_user_groups`): create `user_groups` (id serial primary key, name varchar(255) not null, code varchar(255) null with an index, description text null, permissions text null, created_at and updated_at timestamp null) and `users_groups` (user_id integer not null, user_group_id integer not null, primary key (user_id, user_group_id) named user_group), then insert the Guest/guest and Registered/registered rows with PHP's descriptions; Rollback drops both tables. classes/user_groups.go: `UserGroupCodes(ctx, db, userID uint) ([]string, error)` (codes of the user's groups ordered by user_groups.id, null codes skipped) and `HasGroupCode(ctx, db, userID uint, code string) (bool, error)`. The user API payload keeps `"groups":[]` exactly as today (D-25: contract unchanged); do not read the new table in any user-plugin handler.
|
||||
|
||||
(4) parity/schema_diff_test.go: add allowedDiffs entries `user_groups` and `users_groups` with a reason naming D-25 and that the frozen PHP snapshot predates the user-plugin dump (the same justification as user_throttle). Keep every other entry.
|
||||
|
||||
(5) Tests: updates/user_groups_test.go on the plugin's Postgres harness: migrate up creates both tables and the two seed rows, RollbackLast drops them, re-up works; UserGroupCodes returns `admin` for a user linked to a group with code admin and an empty slice for a user with no groups.</action>
|
||||
(5) Tests: updates/user_groups_test.go on the plugin's Postgres harness: migrate up creates both tables and the two seed rows, RollbackLast drops them, re-up works; UserGroupCodes returns `admin` for a user linked to a group with code admin and an empty slice for a user with no groups.
|
||||
|
||||
(6) Commits: commit the user-plugin files inside the submodule (`git -C ../fonoteka.go/plugins/golem15/user`), push its master, then commit parity/schema_diff_test.go together with the bumped submodule pointer in fonoteka.go, no co-author tags.</action>
|
||||
<verify>
|
||||
<automated>go vet ./... && go test ./modules/beachcomber/... -count=1 -v -run '^(TestSearchPage.*|TestTypesenseSearchPage.*)$' && go test ./cmd/summer -run TestDocsTree -count=1 && go -C ../fonoteka.go vet ./... && go -C ../fonoteka.go test ./plugins/golem15/user/updates -count=1 -v -run '^(TestUserGroups.*)$' && go -C ../fonoteka.go test ./parity -run '^(TestSchemaMatchesPHPSnapshot|TestUserAPINuxtFlows)$' -count=1</automated>
|
||||
<fails_when>Any command exits non-zero; a verbose run prints "no tests to run", "--- FAIL" or "--- SKIP", or lacks "--- PASS" for a TestSearchPage test and a TestUserGroups test; TestSchemaMatchesPHPSnapshot reports an extra Go table; TestUserAPINuxtFlows fails (user payload changed).</fails_when>
|
||||
@@ -283,7 +285,7 @@ Detector run on the Phase 12 ROADMAP section: detected=false. Considered D-25 (u
|
||||
- `grep -c 'PageSearcher' modules/beachcomber/README.md` and `grep -c 'PageSearcher' docs/services/search.md` each print at least 1.
|
||||
- `grep -c '"user_groups"' ../fonoteka.go/parity/schema_diff_test.go` and `grep -c '"users_groups"' ../fonoteka.go/parity/schema_diff_test.go` each print 1.
|
||||
- `grep -c 'json:"-"' ../fonoteka.go/plugins/golem15/user/models/user.go` increases by one relative to HEAD (the Groups field is never serialized).
|
||||
- `grep '"groups":' ../fonoteka.go/plugins/golem15/user/controllers/api_controller.go | grep -c '\[\]any{}'` prints 1, and this task's fonoteka.go commit touches no file under `plugins/golem15/user/controllers/` (payload untouched).
|
||||
- `grep '"groups":' ../fonoteka.go/plugins/golem15/user/controllers/api_controller.go | grep -c '\[\]any{}'` prints 1, and this task's sm-user-plugin commit touches no file under `controllers/` (payload untouched).
|
||||
</acceptance_criteria>
|
||||
<done>Callers can ask the search engine for one page of candidate ids and the total it found, ranked with explicit weights, and any plugin can ask which group codes a user has without the user API changing.</done>
|
||||
</task>
|
||||
|
||||
@@ -142,7 +142,7 @@ Repo: fonoteka.go only (summercms.go untouched). No Nuxt or fonoteka-mcp change.
|
||||
@../fonoteka.go/parity/README.md
|
||||
|
||||
<interfaces>
|
||||
- From 12-01: `lagoon.ValidateRequest(ctx, tx, input, []lagoon.RequestRule{{Field, Rules: lagoon.ParseRules("...")}}, tr)`, `lagoon.UploadedFileFromHeader`, `attach.PublicURL`, `(*attach.File).URL()`, tide `Request.Parts`, user plugin `classes.HasGroupCode(ctx, db, userID, "admin")`.
|
||||
- From 12-01: `lagoon.ValidateRequest(ctx, tx, input, []lagoon.RequestRule{{Field, Rules: lagoon.ParseRules("...")}}, tr)`, `lagoon.UploadedFileFromHeader`, `attach.PublicURL`, `(*attach.File).URL()`, tide `Request.Parts`, user plugin `classes.HasGroupCode(ctx, db, userID, "admin")` (import path `git.golem15.com/golem15/sm-user-plugin/classes`).
|
||||
- fonoteka today: `classes.AccessibleByMembership(userID)`, `classes.ResolveActiveCollection(ctx, gdb, userID)` (JWT default path only; callers: controllers/genre_controller.go ListGenres and controllers/api/oauth_consent_controller.go), `persistContext` (INSERT ... ON CONFLICT (user_id)), `classes.SaveCollection(ctx, gdb, c, requested)` with `CollectionFillFields = {name, description}`, `classes.FieldErrors`, `classes.SerializeCollection` (4-key stub), `models.Collection{ID, OwnerID, Owner, Name, Description, Kind, PublicToken, PublicEnabled, PublicTokenGeneratedAt, ReservationsAllowed, Editors, DeletedAt, CreatedAt, UpdatedAt}` with `BeforeDelete` doing a bulk album delete, `models.ApiToken{CollectionIDs lagoon.Jsonable[[]uint], Scopes}`, `models.UserCollectionContext`, `models.OrgAiCredential`, `models.UserAiCredential`, `models.UserDiscogsCredential`, `models.OrgDiscogsCredential`, `models.AlbumMarketCurrencies`.
|
||||
- Request context: `bouncer.User(ctx) (*bouncer.Principal, bool)`; the personal token is `cred, _ := bouncer.Credential(ctx); tok, _ := cred.(*models.ApiToken)` (middleware/token_scope.go); `wire.WriteJSON` (no trailing newline), `wire.WriteOpaque500`; responses carry `Cache-Control: no-cache, private` and `Content-Type: application/json`.
|
||||
- surf: `g.Get/Post/Put/Delete(path, h, mw...)`, `g.Where(param, regex)` constrains only the immediately preceding route (Pitfall 2), inline `throttle:N,M`, `body.limit:<bytes>`.
|
||||
|
||||
@@ -196,7 +196,7 @@ Repos: summercms.go (framework tests, gate script, planning docs) and fonoteka.g
|
||||
|
||||
(3) scripts/check-phase12.sh modelled on check-phase11.sh: stages `--go` (vet and test both repos), `--parity` (TestParityCorpus with 99 ported and 0 failing parsed from the coverage line, TestBroadcastGoldens all four passing, TestFonotekaNuxtFlows both subtests, check_corpus --require-recorded --check-secrets), `--named` (every test named in 12-VALIDATION.md run by exact name with -run '^Name$', refusing skip, fail and "no tests to run"), `--removal` (anchor-exact mutations restored byte for byte with cmp: drop AlbumsAccessibleBy from the search re-gate, drop the token narrowing in AccessibleBy, drop the owner check in share, add owner_id to CollectionFillFields, drop the sha256 lookup in accept, swap PublicOnly for no policy in the manual fetcher, make the invitation args carry the plaintext token; each must make its named test fail on an assertion), `--coverage` (go test -coverprofile per listed package; refuse any package below 80% with its number), `--evidence` (12-SECURITY-REVIEW.md lists every T-12 id with a passing test, 12-VALIDATION.md has no pending or TBD row and nyquist_compliant true), `--all`, and `--self-test` proving each detector fails closed on planted inputs. It never names the application in framework-facing output beyond the paths it must call.
|
||||
|
||||
(4) Planning docs (separate commit): 12-SECURITY-REVIEW.md (reviewer note: self-performed by the executor if no reviewer agent can be spawned, per the 08-10 precedent; table of T-12-01..T-12-34 and T-12-SC with category, severity, disposition, protecting file and the test name that was run and seen failing under --removal); 12-VALIDATION.md: replace the seeded Per-Task Verification Map rows with the final rows (task ids 12-01-T1..12-05-T3, the exact commands each plan ran, file-exists ticks, statuses), tick Wave 0 items, set status validated, nyquist_compliant true, wave_0_complete true; REQUIREMENTS.md traceability rows API-01 and API-02 set to Complete and their checkboxes ticked.</action>
|
||||
(4) Planning docs (separate commit): 12-SECURITY-REVIEW.md (reviewer note: self-performed by the executor if no reviewer agent can be spawned, per the 08-10 precedent; table of T-12-01..T-12-34 and T-12-SC with category, severity, disposition, protecting file and the test name that was run and seen failing under --removal); 12-VALIDATION.md: replace the seeded Per-Task Verification Map rows with the final rows (task ids 12-01-T1..12-05-T3, the exact commands each plan ran, file-exists ticks, statuses), tick Wave 0 items, set status validated, nyquist_compliant true, wave_0_complete true; REQUIREMENTS.md traceability rows API-01 and API-02 set to Complete and their checkboxes ticked. The user plugin test file lives in the sm-user-plugin submodule; commit it there and push its master, then commit the bumped pointer in fonoteka.go with the other fonoteka.go test changes.</action>
|
||||
<verify>
|
||||
<automated>scripts/check-phase12.sh --self-test && scripts/check-phase12.sh --all</automated>
|
||||
<fails_when>Non-zero exit; output contains "refuse:" or "FAIL", a package coverage line below 80%, a named test reported skipped or with "no tests to run", a --removal mutation whose named test still passes, or the evidence stage reporting a pending row or a T-12 id without a test.</fails_when>
|
||||
|
||||
Reference in New Issue
Block a user