docs(quick-261011-mag): fix check-phase10.sh hygiene gaps from 10-VERIFICATION.md
This commit is contained in:
@@ -5,10 +5,10 @@ current_phase: 11
|
||||
current_phase_name: Jobs, realtime and search infrastructure
|
||||
status: planning
|
||||
stopped_at: Phase 16.3 complete, ready to plan Phase 11
|
||||
last_updated: "2026-10-11T13:20:59.821Z"
|
||||
last_updated: "2026-10-11T14:14:17.635Z"
|
||||
last_activity: 2026-10-11
|
||||
last_activity_desc: "Completed quick task 261011-kzi: Fix Phase 09 gaps (fonoteka route inventory, per-guard token throttle)"
|
||||
state_head: b45af8ebbf67ef90e67b2ad701dddc4e5c7e645a
|
||||
last_activity_desc: "Completed quick task 261011-mag: fix check-phase10.sh hygiene gaps from 10-VERIFICATION.md"
|
||||
state_head: 1541cd4167bb9a92ffba897403a967030f37b93d
|
||||
progress:
|
||||
total_phases: 40
|
||||
completed_phases: 13
|
||||
@@ -33,7 +33,7 @@ See: .planning/PROJECT.md (updated 2026-09-16)
|
||||
Phase: 11 — Jobs, realtime and search infrastructure
|
||||
Plan: Not started
|
||||
Status: Ready to plan
|
||||
Last activity: 2026-10-11 - Completed quick task 261011-kzi: Fix Phase 09 gaps (fonoteka route inventory, per-guard token throttle)
|
||||
Last activity: 2026-10-11 - Completed quick task 261011-mag: fix check-phase10.sh hygiene gaps from 10-VERIFICATION.md
|
||||
|
||||
Progress: [████░░░░░░] 41%
|
||||
|
||||
@@ -762,6 +762,7 @@ Recent decisions affecting current work:
|
||||
| 261011-joh | sieve: generic collection helpers module (Map, TryMap, Filter, Reduce, KeyBy, GroupBy, Uniq, UniqBy, Partition, Find, Set) | 2026-10-11 | 6933004, a0bf8ec | [261011-joh-sieve-generic-collection-helpers-module](./quick/261011-joh-sieve-generic-collection-helpers-module/) |
|
||||
| 30 | go test -short runs without Docker: TestMain flag parsing fix in 4 modules, cabana list fixture skip | 2026-10-11 | 7ef2e4f, c85cb64 | — |
|
||||
| 261011-kzi | Fix Phase 09 gaps: fonoteka phase09AdminRoutes inventory + per-guard cabana last_used_at throttle | 2026-10-11 | b45af8e | [261011-kzi-fix-phase-09-gaps-add-the-10-post-phase-](./quick/261011-kzi-fix-phase-09-gaps-add-the-10-post-phase-/) |
|
||||
| 261011-mag | fix check-phase10.sh hygiene gaps from 10-VERIFICATION.md | 2026-10-11 | 1541cd4 | [261011-mag-fix-check-phase10-sh-hygiene-gaps-from-1](./quick/261011-mag-fix-check-phase10-sh-hygiene-gaps-from-1/) |
|
||||
|
||||
## Deferred Items
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
phase: quick-261011-mag
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- scripts/check-phase10.sh
|
||||
- admin/tests/form/mlLocale.test.ts
|
||||
- admin/tests/form/MLField.test.ts
|
||||
- admin/tests/form/FileCaptionModal.test.ts
|
||||
- admin/tests/account/ApiTokensPanel.test.ts
|
||||
- admin/tests/account/ApiTokenCreateDialog.test.ts
|
||||
autonomous: true
|
||||
requirements: [QUICK-261011-mag]
|
||||
|
||||
estimate:
|
||||
tokens: 40000
|
||||
raw_tokens: 40000
|
||||
tasks: 3
|
||||
confidence: low
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "`bash scripts/check-phase10.sh --hygiene` exits 0 and prints `phase10 hygiene passed` (live at planning time it exits 1 with 7 refusals)."
|
||||
- "`bash scripts/check-phase10.sh --self-test` exits 0 and prints `phase10 self-test passed`. Its plant loop now also plants a `new XMLHttpRequest()` outside admin/src/api/files.ts and an extra file in admin/src/api; each is refused for its own rule (`direct fetch` / `unexpected files in admin/src/api`), never for `imported by no test`."
|
||||
- "The direct-network rule exempts exactly one line: admin/src/api/files.ts `const request = new XMLHttpRequest()` (the upload-progress XHR from 12.2-04, T-12.2-32). The admin/src/api listing rule exempts exactly the path admin/src/api/files.ts. Every other hygiene rule is byte-for-byte unchanged."
|
||||
- "Each of admin/src/components/account/ApiTokenCreateDialog.vue, admin/src/components/account/ApiTokensPanel.vue, admin/src/components/form/fields/FileCaptionModal.vue, admin/src/components/form/fields/MLField.vue and admin/src/components/form/mlLocale.ts is imported directly by a test that mounts or calls it and asserts behavior (no bare import)."
|
||||
- "`npm --prefix admin run typecheck` and the full `npm --prefix admin test` pass."
|
||||
- "admin/src and modules/boardwalk/dist are unchanged by this task, so the embedded dist needs no rebuild; `scripts/check-admin-dist.sh` exits 0."
|
||||
artifacts:
|
||||
- path: scripts/check-phase10.sh
|
||||
provides: "narrow files.ts exceptions in the network and api-listing rules, plus xhr and apifile self-test plants"
|
||||
contains: "admin/src/api/files"
|
||||
- path: admin/tests/form/mlLocale.test.ts
|
||||
provides: "direct unit test of mlLocale.ts"
|
||||
contains: "src/components/form/mlLocale'"
|
||||
- path: admin/tests/form/MLField.test.ts
|
||||
provides: "direct mount test of the MLField locale wrapper"
|
||||
contains: "src/components/form/fields/MLField.vue'"
|
||||
- path: admin/tests/form/FileCaptionModal.test.ts
|
||||
provides: "direct mount test of the caption modal over a stub FileRoutes"
|
||||
contains: "src/components/form/fields/FileCaptionModal.vue'"
|
||||
- path: admin/tests/account/ApiTokensPanel.test.ts
|
||||
provides: "a directly-mounted ApiTokensPanel describe block next to the existing app-level tests"
|
||||
contains: "src/components/account/ApiTokensPanel.vue'"
|
||||
- path: admin/tests/account/ApiTokenCreateDialog.test.ts
|
||||
provides: "a directly-mounted ApiTokenCreateDialog describe block next to the existing app-level tests"
|
||||
contains: "src/components/account/ApiTokenCreateDialog.vue'"
|
||||
key_links:
|
||||
- from: scripts/check-phase10.sh hygiene_checks
|
||||
to: admin/tests
|
||||
via: "every-module-imported rule greps `src/<path>'` (a .vue keeps its extension, a .ts drops it)"
|
||||
pattern: "spec=\"src/\\$\\{file%.ts\\}\""
|
||||
- from: scripts/check-phase10.sh run_self_test
|
||||
to: hygiene_checks
|
||||
via: "scratch copy of HYGIENE_DIRS must pass clean, every plant must fail for its own rule"
|
||||
pattern: "for plant in vhtml fetch"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Close the three gaps in .planning/phases/10-admin-vue-spa/10-VERIFICATION.md. They share one root cause: the Phase 10 hygiene gate has been red since 12.2-04 (ea33296) because later phases added SPA files without updating the gate or adding direct-import tests.
|
||||
|
||||
Live observation at planning time (HEAD d2fa195), `bash scripts/check-phase10.sh --hygiene` exit 1:
|
||||
- `direct fetch outside admin/src/api/client.ts: admin/src/api/files.ts:150: const request = new XMLHttpRequest()`
|
||||
- `unexpected files in admin/src/api: admin/src/api/files.ts`
|
||||
- `admin/src/components/account/ApiTokenCreateDialog.vue is imported by no test`
|
||||
- `admin/src/components/account/ApiTokensPanel.vue is imported by no test`
|
||||
- `admin/src/components/form/fields/FileCaptionModal.vue is imported by no test`
|
||||
- `admin/src/components/form/fields/MLField.vue is imported by no test`
|
||||
- `admin/src/components/form/mlLocale.ts is imported by no test`
|
||||
|
||||
`bash scripts/check-phase10.sh --self-test` exit 1: `self-test hygiene rejected the clean scratch copy` (a consequence of the seven refusals above).
|
||||
|
||||
The fix is decided per failure:
|
||||
1. **files.ts (two refusals): the gate is stale, so the gate changes.** Both rules assume the Phase 10 world, where admin/src/api held only schema.d.ts, client.ts and types.ts and nothing uploaded files. files.ts holds the FileRoutes adapters. They are API adapters and belong in api/. Every JSON, blob and thumbnail call already goes through the typed `api` client; files.ts has no `fetch(` left. The one XMLHttpRequest exists because fetch has no upload-progress events. That XHR sends X-Requested-With (T-12.2-32, asserted by admin/tests/smoke/deferred.smoke.test.ts) and refreshes through client.ts `refreshSession`. All wire shapes come from the generated aliases in ./types, so SC-4 holds in substance. Moving the XHR into client.ts was rejected: the same call would sit in a different file just to satisfy a filename rule, and the api-listing rule would still flag the adapters. The gate gets narrow, line-anchored exceptions instead, the same pattern as the MarkdownField (27711b7) and listState (9cac45c) precedents. New self-test plants prove the narrowed rules still fail closed.
|
||||
2. **Five untested modules: the rule is current, so the tests change.** The 10-05 rule that every SPA module is imported by a test is still valid. The account components are only exercised through mountApp; the others only through their wrappers or formState re-exports. Each gets a direct, behavior-asserting test. Nothing in admin/src changes, so the committed dist stays valid.
|
||||
3. **--self-test: no separate change.** It goes green once 1 and 2 land. It also gains the two plants from 1.
|
||||
|
||||
Purpose: restore `scripts/check-phase10.sh --hygiene` and `--self-test` to green so the Phase 10 acceptance gate is meaningful again and 10-VERIFICATION can return to human_needed.
|
||||
Output: one gate script edit, three new SPA test files, two extended SPA test files.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.claude/msd-core/workflows/execute-plan.md
|
||||
@~/.claude/msd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@./CLAUDE.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/10-admin-vue-spa/10-VERIFICATION.md
|
||||
@scripts/check-phase10.sh
|
||||
|
||||
Key facts for the executor (verified at planning time, no need to re-derive):
|
||||
- hygiene_checks network rule (scripts/check-phase10.sh around line 360) pipes `grep -rnE '(^|[^A-Za-z0-9_.])fetch\(|XMLHttpRequest\(|axios' admin/src` through `grep -v '^admin/src/api/client.ts:'`. Its output format is `path:line:content`.
|
||||
- api-listing rule (around line 364) is `find admin/src/api -type f ! -name schema.d.ts ! -name client.ts ! -name types.ts`, run from the tree root, so it prints `admin/src/api/<name>`.
|
||||
- every-module rule (around line 388) builds `spec="src/${file%.ts}"` and greps admin/tests `*.ts` for `"$spec'"`. Therefore a `.vue` import must read `'../../src/components/.../X.vue'` (extension kept, single quotes), and the .ts module must be imported as `'../../src/components/form/mlLocale'` (no extension, single quotes).
|
||||
- The app-name rule also scans admin/tests with `pl[yý]tarium|fonoteka|albumy|kolekcj|winyl|p[lł]yt[aęy]` (case-insensitive). Test strings must not match it. Use neutral data (`Hello`/`Cześć`, `acme`, `gadgets`).
|
||||
- admin/tsconfig.json has `noUnusedLocals: true`. vue-tsc fails on an import the test never uses.
|
||||
- Test harness: admin/tests/helpers.ts exports `API`, `mockApi(routes)`, `requestsTo`, `resetState`, `keydown`, `wait`, `mountApp`. admin/tests/form/FileuploadField.test.ts shows the pattern for mounting a component directly with `mount` + `mockApi`. admin/tests/form/MLFields.test.ts shows providing `FORM_ENABLED_LOCALES` (from src/components/form/formContext).
|
||||
- FieldControlProps (src/components/form/control.ts): field, modelValue, controlId, optional invalid, describedBy, labels, source, recordId.
|
||||
- FileCaptionModal props: open, file (FileItem|null), routes (FileRoutes|null), preview (string|null), idPrefix. Emits: update:open, saved(file), closed. It renders through a reka-ui portal into document.body with `[data-caption-modal]`, `[data-caption-title]`, `[data-caption-description]`, `[data-action="save-caption"]`. Save calls `routes.update(file.id, { title, description })`. On data it emits saved, then update:open false. A 422 keeps the dialog open and shows the detail messages. Any other failure posts a danger toast.
|
||||
- ApiTokensPanel props: `confirm: ReturnType<typeof useConfirm>` (src/components/ui/confirm). On mount it calls `GET /account/tokens`, then renders `[data-token-row="<id>"]`, `[data-tokens-empty]` or `[data-tokens-error]`. ApiTokenCreateDialog props: open, confirm. Emits update:open, created, closed. It loads `GET /account/tokens/scopes` and posts `POST /account/tokens`. It renders `[data-token-dialog]`, `[data-action="create-token-submit"]`, `[data-token-plaintext]`, `[data-action="hide-token"]`. The existing tests in admin/tests/account/ already define TOKENS, SCOPES, envelope, existingToken, createdToken, PLAIN and scopes fixtures. Reuse them.
|
||||
- admin/node_modules is installed (vitest and vue-tsc present).
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="tracer">
|
||||
<name>Task 1: Narrow the check-phase10.sh network and api-listing rules for the files.ts upload XHR, with fail-closed self-test plants</name>
|
||||
<files>scripts/check-phase10.sh</files>
|
||||
<action>
|
||||
Edit only `hygiene_checks` and `run_self_test` in scripts/check-phase10.sh. Do NOT touch admin/src/api/files.ts or admin/src/api/client.ts. The objective explains why: the gate is stale, and the code is sound.
|
||||
|
||||
(a) Network rule: keep the existing `grep -v '^admin/src/api/client.ts:'` filter. Chain a second filter, `grep -vE`, that drops only a line matching, anchored at both ends: path `admin/src/api/files.ts`, a numeric line number, optional leading whitespace, the exact statement `const request = new XMLHttpRequest()` and optional trailing whitespace. Escape the dots and parentheses as in the MarkdownField allowlist two rules above. Keep the `|| true` and the unchanged `fail "direct fetch outside admin/src/api/client.ts: $hits"` message, because the self-test matches on `direct fetch`. Put a one-line comment above the rule in the style of the MarkdownField comment. It should say: uploads in admin/src/api/files.ts use XMLHttpRequest because only XHR reports upload progress (12.2-04); that XHR sends X-Requested-With (T-12.2-32) and refreshes through client.ts; every other XHR, fetch or axios call is still refused.
|
||||
|
||||
(b) api-listing rule: add `! -path admin/src/api/files.ts` to the find. Do not use `! -name files.ts`; it would also exempt a nested files.ts. Update the comment above it so the directory holds the generated schema, the client, the aliases and the FileRoutes adapters (files.ts, 12.2-04). Keep the fail message `unexpected files in admin/src/api` unchanged.
|
||||
|
||||
(c) run_self_test plant loop: extend `for plant in vhtml fetch appname storage` with two more plants.
|
||||
- `xhr`: write `export const open = () => new XMLHttpRequest()` to `$scratch/admin/src/__plant/plant.ts`, with want=`direct fetch`. The default scratch test import already covers it.
|
||||
- `apifile`: write `export {}` to `$scratch/admin/src/api/__plant.ts`, write the scratch test `$scratch/admin/tests/__plant.test.ts` as `import '../src/api/__plant'` so that the import rule is satisfied, and set want=`unexpected files in admin/src/api`.
|
||||
At the top of each iteration, also remove `$scratch/admin/src/api/__plant.ts` next to the existing `rm -rf` of `__plant` and the scratch test, so the clean-copy state holds between plants. Update the comment above the scratch block that lists what gets planted.
|
||||
|
||||
Leave the `vhtml`, `fetch`, `appname` and `storage` plants, `HYGIENE_DIRS`, the other rules and all non-hygiene stages unchanged.
|
||||
|
||||
Commit on its own: `fix(scripts): allow the files.ts upload XHR in check-phase10 hygiene`. Stage only `scripts/check-phase10.sh` by path. Other sessions may have uncommitted edits, so never use `git add -A`. No co-author trailer (user rule).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>bash -c 'cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && bash -n scripts/check-phase10.sh && out="$(bash scripts/check-phase10.sh --hygiene 2>&1 || true)"; echo "$out"; ! grep -q "admin/src/api/files.ts" <<<"$out" && [ "$(grep -v "imported by no test" <<<"$out" | grep -c "refuse:")" -eq 0 ] && grep -q "for plant in vhtml fetch appname storage xhr apifile" scripts/check-phase10.sh && grep -q -- "! -path admin/src/api/files.ts" scripts/check-phase10.sh'</automated>
|
||||
</verify>
|
||||
<done>The hygiene stage no longer refuses admin/src/api/files.ts. The only refusals left are the five `imported by no test` lines that Tasks 2 and 3 close. The script parses (`bash -n`), and the self-test loop names the xhr and apifile plants. The change is committed alone.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Direct tests for mlLocale.ts, MLField.vue and FileCaptionModal.vue</name>
|
||||
<files>admin/tests/form/mlLocale.test.ts, admin/tests/form/MLField.test.ts, admin/tests/form/FileCaptionModal.test.ts</files>
|
||||
<behavior>
|
||||
- mlLocale: isMLFieldType is true for mltext, mltextarea and mlmarkdown, and false for text and undefined. isMLField is true for a translatable field with an element, false without an element, false without translatable:true, and false for undefined. localeRecord keeps only string values, drops numbers and null, and returns {} for null, an array or a string. broadcastMLLocale('pl') dispatches a window CustomEvent named ML_LOCALE_CHANGE with detail 'pl'. broadcastMLLocale('') dispatches nothing.
|
||||
- MLField: mounted with inner=TextField, innerType='text', mlType='mltext', FORM_ENABLED_LOCALES=['en','pl'] and modelValue {en:'Hello', pl:'Cześć'}. The root carries data-ml-field="mltext", the locale select lists en and pl, and the input shows 'Hello'. Typing emits update:modelValue with the whole map, with only en changed. Choosing pl shows 'Cześć' and broadcasts, so a second mounted MLField switches to pl. A window ML_LOCALE_CHANGE event switches the locale. With contentLocale=true, an inner stub that declares a contentLocale prop receives the active locale. Without it, the stub receives no contentLocale.
|
||||
- FileCaptionModal: mounted open with a FileItem and a stub FileRoutes whose methods are vi.fn(). The title and description inputs are prefilled from the file. Save calls routes.update(file.id, {title, description}) with the edited values and emits saved(file), then update:open false. A 422 whose details name title keeps the dialog open, shows that message and emits no saved. A 500 posts a danger toast (state/useToasts toasts) and keeps the typed input.
|
||||
</behavior>
|
||||
<action>
|
||||
Create three new Vitest files under admin/tests/form/. They test existing code, so there is no RED phase. The tests must pass against the current admin/src, and admin/src must not change. If a behavior above turns out to differ from the code, assert what the code does and record the discrepancy in the SUMMARY. Do not edit the component: that would force a dist rebuild and is outside this task.
|
||||
|
||||
mlLocale.test.ts imports `ML_LOCALE_CHANGE`, `broadcastMLLocale`, `isMLField`, `isMLFieldType` and `localeRecord` from `'../../src/components/form/mlLocale'`: single quotes, no extension, because that exact string is what the gate looks for. Listen with window.addEventListener and remove the listener afterwards.
|
||||
|
||||
MLField.test.ts imports `MLField from '../../src/components/form/fields/MLField.vue'` and `TextField from '../../src/components/form/fields/TextField.vue'`. Mount with `mount` from @vue/test-utils, providing `FORM_ENABLED_LOCALES` as admin/tests/form/MLFields.test.ts does. Use `enableAutoUnmount(afterEach)` so the window listeners are removed. For the contentLocale cases, define a small inner stub with defineComponent that declares `contentLocale` as a prop and renders it in a data attribute.
|
||||
|
||||
FileCaptionModal.test.ts imports `FileCaptionModal from '../../src/components/form/fields/FileCaptionModal.vue'`. Build a FileItem literal typed through `'../../src/api/types'`, or take one entry from `deferredFilesFixture` in admin/tests/fixtures/typed.ts. Build a `FileRoutes` stub typed through `'../../src/api/files'`, so no HTTP is involved. Mount with `attachTo: document.body` and query `document.body` for the portal content. Call `resetState()` from ../helpers in beforeEach, as the sibling tests do. The language bundle is not loaded, so visible texts are their keys; assert on data attributes and emitted events, not translated text.
|
||||
|
||||
All test strings must stay clear of the gate's app-name pattern (see context).
|
||||
|
||||
Commit after Task 3 together with the account tests as one `test(admin): ...` commit, or as its own commit. Either way, stage these files by path only.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>bash -c 'cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && npm --prefix admin test -- tests/form/mlLocale.test.ts tests/form/MLField.test.ts tests/form/FileCaptionModal.test.ts && npm --prefix admin run typecheck && out="$(bash scripts/check-phase10.sh --hygiene 2>&1 || true)"; ! grep -qE "(mlLocale\.ts|MLField\.vue|FileCaptionModal\.vue) is imported by no test" <<<"$out"'</automated>
|
||||
</verify>
|
||||
<done>Three new test files pass in Vitest and under vue-tsc. The hygiene stage no longer reports mlLocale.ts, MLField.vue or FileCaptionModal.vue as untested. admin/src is untouched.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 3: Direct-mount tests for ApiTokensPanel and ApiTokenCreateDialog, then run the full gate</name>
|
||||
<files>admin/tests/account/ApiTokensPanel.test.ts, admin/tests/account/ApiTokenCreateDialog.test.ts</files>
|
||||
<behavior>
|
||||
- ApiTokensPanel mounted directly with `confirm: useConfirm()`. With GET /account/tokens answering [existingToken], `[data-token-row="11"]` renders with the token name. With an empty list, `[data-tokens-empty]` renders. With a 500, `[data-tokens-error]` renders. Each case makes exactly one GET to the tokens endpoint.
|
||||
- ApiTokenCreateDialog mounted directly with `open: true` and `confirm: useConfirm()`. `[data-token-dialog]` renders in document.body and the scopes from GET /account/tokens/scopes are offered. Filling the name, picking a scope and submitting sends one POST /account/tokens. The plaintext PLAIN then appears in `[data-token-plaintext]` and `created` is emitted.
|
||||
</behavior>
|
||||
<action>
|
||||
Extend the two existing files. Do not create sibling files. Add a new `describe('... mounted directly')` block to each and leave the existing mountApp-based tests unchanged.
|
||||
|
||||
In ApiTokensPanel.test.ts, add `mount` to the existing `@vue/test-utils` import. Add `import ApiTokensPanel from '../../src/components/account/ApiTokensPanel.vue'` (single quotes, extension kept) and `import { useConfirm } from '../../src/components/ui/confirm'`. Add `mockApi` to the ../helpers import. Reuse TOKENS, envelope and existingToken.
|
||||
|
||||
In ApiTokenCreateDialog.test.ts, add the same `mount`, `useConfirm` and `mockApi` imports. Add `import ApiTokenCreateDialog from '../../src/components/account/ApiTokenCreateDialog.vue'`. Reuse TOKENS, SCOPES, envelope, scopes, createdToken and PLAIN. Copy the field-filling and submit steps from the file's existing `step 1` and `step 2` describes; do not invent selectors. The dialog portals into document.body, so mount with `attachTo: document.body`.
|
||||
|
||||
Every new import must be used, because noUnusedLocals is on. Keep test data neutral (see context). Do not change admin/src.
|
||||
|
||||
Then run the full verification below. If `--self-test` fails on a plant for the wrong reason, fix the plant in scripts/check-phase10.sh, as a follow-up to the Task 1 commit. Do not loosen a rule.
|
||||
|
||||
Commit the five test files as `test(admin): import the untested SPA modules directly for check-phase10 hygiene`. Stage the five paths explicitly; no co-author trailer. Leave other sessions' uncommitted files (STATE.md, state.json, .msd/) unstaged.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>bash -c 'cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && bash scripts/check-phase10.sh --hygiene && bash scripts/check-phase10.sh --self-test && npm --prefix admin run typecheck && npm --prefix admin test && scripts/check-admin-dist.sh && git diff --quiet HEAD -- admin/src modules/boardwalk/dist'</automated>
|
||||
</verify>
|
||||
<done>`check-phase10.sh --hygiene` prints `phase10 hygiene passed` and `--self-test` prints `phase10 self-test passed`, both exit 0. The full Vitest suite and vue-tsc pass, the dist drift gate passes, and admin/src and the dist are unchanged. The tests are committed separately from the gate change.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| SPA → admin API (cookie-authenticated) | Every browser request must carry X-Requested-With (CSRF, T-10 / T-12.2-32) and go through the typed client or its sanctioned upload path |
|
||||
| Phase gate → future commits | check-phase10.sh hygiene is the mechanical guard for SC-4; an over-broad exception silently admits untyped or unguarded network calls |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-261011-mag-01 | Tampering | check-phase10.sh network rule exception | medium | mitigate | The exception is anchored at both ends to one path and one exact statement (`const request = new XMLHttpRequest()` in admin/src/api/files.ts). The new self-test `xhr` plant proves an XHR in any other file is still refused with `direct fetch`. |
|
||||
| T-261011-mag-02 | Tampering | check-phase10.sh api-listing exception | low | mitigate | The exemption uses `! -path admin/src/api/files.ts` (exact path, not `-name`). The new `apifile` plant proves any other file added to admin/src/api is refused. |
|
||||
| T-261011-mag-03 | Spoofing (CSRF) | admin/src/api/files.ts upload XHR | high | mitigate | The exception is justified only because the XHR sets X-Requested-With. That property stays asserted by admin/tests/smoke/deferred.smoke.test.ts ("uploads to record 0 with the form key", T-12.2-32) and server-side TestPhase10CSRF. The full `npm --prefix admin test` in Task 3 re-runs it. |
|
||||
| T-261011-mag-SC | Tampering | npm/pip/cargo installs | low | accept | No package installs. The tests use the already-installed admin/node_modules from the committed lockfile. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- `bash scripts/check-phase10.sh --hygiene`: exit 0
|
||||
- `bash scripts/check-phase10.sh --self-test`: exit 0 (clean copy passes; vhtml, fetch, appname, storage, xhr and apifile plants each refused for their own rule)
|
||||
- `npm --prefix admin run typecheck` and `npm --prefix admin test`: pass
|
||||
- `scripts/check-admin-dist.sh`: exit 0; `git diff --quiet HEAD -- admin/src modules/boardwalk/dist`
|
||||
- Optional for the re-verifier, not required here (needs Docker and the fonoteka.go sibling): `bash scripts/check-phase10.sh --all`
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
The three 10-VERIFICATION gaps are closed by live evidence. The SC-4 hygiene stage is green with exactly two narrow, documented, self-tested exceptions for the files.ts upload path. All five later-phase SPA modules are imported directly by behavior-asserting tests. `--self-test` passes. No production code or embedded dist changed.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/quick/261011-mag-fix-check-phase10-sh-hygiene-gaps-from-1/261011-mag-SUMMARY.md` when done. Record under decisions why files.ts is exempted in the gate rather than restructured, citing T-12.2-32 and the MarkdownField/listState precedents.
|
||||
</output>
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
phase: quick-261011-mag
|
||||
plan: 01
|
||||
subsystem: admin-spa / phase gates
|
||||
tags: [check-phase10, hygiene, vitest, admin]
|
||||
status: complete
|
||||
requires: []
|
||||
provides:
|
||||
- "scripts/check-phase10.sh hygiene: narrow files.ts upload-XHR and api-listing exceptions with xhr/apifile self-test plants"
|
||||
- "direct tests for mlLocale.ts, MLField.vue, FileCaptionModal.vue, ApiTokensPanel.vue, ApiTokenCreateDialog.vue"
|
||||
affects: [phase-10 verification]
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: ["line-anchored gate allowlist (MarkdownField/listState precedent)", "self-test plant per narrowed rule"]
|
||||
key-files:
|
||||
created:
|
||||
- admin/tests/form/mlLocale.test.ts
|
||||
- admin/tests/form/MLField.test.ts
|
||||
- admin/tests/form/FileCaptionModal.test.ts
|
||||
modified:
|
||||
- scripts/check-phase10.sh
|
||||
- admin/tests/account/ApiTokensPanel.test.ts
|
||||
- admin/tests/account/ApiTokenCreateDialog.test.ts
|
||||
decisions:
|
||||
- "admin/src/api/files.ts is exempted in the gate rather than restructured: its single XMLHttpRequest exists because only XHR reports upload progress (12.2-04), it sends X-Requested-With (T-12.2-32, asserted by tests/smoke/deferred.smoke.test.ts) and refreshes through client.ts; moving it into client.ts would only satisfy a filename rule. The exception follows the MarkdownField (27711b7) and listState (9cac45c) precedents: one line-anchored statement and one exact path, each proven fail-closed by a new self-test plant."
|
||||
metrics:
|
||||
duration: "~10 min"
|
||||
completed: 2026-10-11
|
||||
actuals:
|
||||
tokens: 5750
|
||||
tasks: 3
|
||||
commits: 2
|
||||
plan_head_before: d2fa195a91343fc9d1bd38969c0e830992f41c51
|
||||
plan_head_after: 1541cd4167bb9a92ffba897403a967030f37b93d
|
||||
---
|
||||
|
||||
# Quick 261011-mag Plan 01: check-phase10 hygiene gaps Summary
|
||||
|
||||
The Phase 10 hygiene gate is green again. It now has two narrow exceptions for the upload XHR in admin/src/api/files.ts, each guarded by a self-test plant that proves the rule still refuses everything else. Five later-phase SPA modules that no test imported now have direct, behavior-asserting tests. Nothing in admin/src or the embedded dist changed.
|
||||
|
||||
## Tasks
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
| ---- | ---- | ------ | ----- |
|
||||
| 1 | Narrow the network and api-listing rules for the files.ts upload XHR, add xhr/apifile plants | 42b5cf8 | scripts/check-phase10.sh |
|
||||
| 2 | Direct tests for mlLocale.ts, MLField.vue, FileCaptionModal.vue | 1541cd4 | admin/tests/form/{mlLocale,MLField,FileCaptionModal}.test.ts |
|
||||
| 3 | Direct-mount tests for ApiTokensPanel and ApiTokenCreateDialog, full gate run | 1541cd4 | admin/tests/account/{ApiTokensPanel,ApiTokenCreateDialog}.test.ts |
|
||||
|
||||
Tasks 2 and 3 share one `test(admin)` commit, as the plan allowed.
|
||||
|
||||
## Gate changes (scripts/check-phase10.sh)
|
||||
|
||||
- Network rule: a second filter, `grep -vE '^admin/src/api/files\.ts:[0-9]+:[[:space:]]*const request = new XMLHttpRequest\(\)[[:space:]]*$'`, follows the existing client.ts filter. The fail message is unchanged.
|
||||
- api-listing rule: `! -path admin/src/api/files.ts` (exact path, not `-name`). The comment now names the FileRoutes adapters.
|
||||
- Self-test: the plant loop is now `vhtml fetch appname storage xhr apifile`. `xhr` plants `new XMLHttpRequest()` in `admin/src/__plant/plant.ts` and expects `direct fetch`. `apifile` plants `admin/src/api/__plant.ts` (imported by the scratch test) and expects `unexpected files in admin/src/api`. Each iteration also removes `admin/src/api/__plant.ts`.
|
||||
|
||||
## Verification
|
||||
|
||||
- `bash scripts/check-phase10.sh --hygiene`: `phase10 hygiene passed` (exit 0)
|
||||
- `bash scripts/check-phase10.sh --self-test`: `phase10 self-test passed` (exit 0)
|
||||
- `npm --prefix admin run typecheck`: exit 0
|
||||
- `npm --prefix admin test`: 83 files, 1279 tests passed
|
||||
- `scripts/check-admin-dist.sh`: `modules/boardwalk/dist matches a fresh build` (exit 0)
|
||||
- `git diff --quiet HEAD -- admin/src modules/boardwalk/dist`: unchanged
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Behavior differing from the plan text (code asserted as is, component untouched)
|
||||
|
||||
**1. ApiTokenCreateDialog loads scopes only on an open transition.** The plan said to mount it with `open: true`. Its `watch(() => props.open, ...)` is not `immediate`, so a dialog mounted already open never calls `GET /account/tokens/scopes`. ApiTokensPanel always mounts it closed and then opens it. The direct test does the same: it mounts with `open: false`, then calls `setProps({ open: true })`. No source change was made, which also avoids a dist rebuild.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] FileCaptionModal test fixture typing.** Spreading `deferredFilesFixture.data[0]` made every field optional under `noUncheckedIndexedAccess`, so vue-tsc failed. The fix uses a literal `FileItem` with the same values. Commit 1541cd4.
|
||||
|
||||
**2. [Rule 1 - Bug] Vue reactive-component warning in MLField test.** test-utils makes props reactive, so passing `inner` as a prop triggered Vue's "Component that was made a reactive object" warning. The inner component is now wrapped in `markRaw`. Commit 1541cd4.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: admin/tests/form/mlLocale.test.ts, admin/tests/form/MLField.test.ts, admin/tests/form/FileCaptionModal.test.ts
|
||||
- FOUND: commits 42b5cf8, 1541cd4
|
||||
Reference in New Issue
Block a user