Skip to content

feat: comments - #7

Merged
Philipinho merged 1 commit into
masterfrom
comments
Nov 9, 2023
Merged

Philipinho merged 1 commit into
masterfrom
comments

Conversation

@Philipinho

Copy link
Copy Markdown
Member
  • create comment
  • reply to comment thread
  • edit comment
  • delete comment
  • resolve comment

* create comment
* reply to comment thread
* edit comment
* delete comment
* resolve comment
@Philipinho
Philipinho merged commit cf65fa3 into master Nov 9, 2023
@Philipinho
Philipinho deleted the comments branch February 26, 2024 11:38
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jun 24, 2026
…most)

Adds floatLeft / floatRight image alignment so text wraps beside the image,
beyond the existing block left/center/right. Ported from Forkmost PR docmost#7 /
upstream Docmost PR docmost#1132 (fuscodev), adapted to gitmost's imperative image
node-view (the upstream uses a React styled component; ours styles the node-view
container directly via applyAlignment).

- editor-ext image.ts: `setImageAlign` accepts `floatLeft`/`floatRight`;
  `applyAlignment` resets float/padding then, for a float mode, sets
  `float:left|right` + side padding on the (shrink-to-fit) container so text
  flows beside it (the inner <img> already has max-width:100%). The resolved
  align is mirrored onto the container as `data-image-align` for the responsive
  rule. `data-align` already round-trips the value through parse/renderHTML, so
  float survives serialization / collab / history with no schema change.
- image-menu.tsx: Float-left / Float-right bubble-menu buttons (IconFloatLeft/
  Right) with active state.
- image-resize.module.css: on narrow screens (<=600px) a floated image collapses
  to full width and drops the float (`!important`, keyed on data-image-align) —
  the upstream "100% width on small screen" follow-up.
- i18n: en-US + ru-RU strings.

editor-ext build + client tsc --noEmit clean. Visual wrap behavior is best
confirmed in-browser (logic/serialization verified by build + types).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jun 25, 2026
docmost#159)

The only test command in CI was `pnpm -r test` (unit `.spec.ts` on mocks).
`test:int` (`.int-spec.ts`, real Postgres/Redis) ran nowhere in CI — there
were no DB `services:` — so the cost-cap, FK-cascade, jsonb round-trip and
real AI-apply integration tests never gated a PR, and regressions in those
high-severity paths stayed green (red-team finding docmost#7).

Add `services: postgres (pgvector) + redis` and a `pnpm --filter server
test:int` step. The pgvector image is required because migrations create
vector columns and global-setup runs `CREATE EXTENSION vector`. Service
credentials/db match the defaults in apps/server/test/integration (docmost /
docmost_dev_pw, maintenance db `docmost`, redis 6379), so no TEST_*_URL
overrides are needed; global-setup drops/recreates the isolated docmost_test
DB and migrates it.

NOTE: the workflow change itself can only be validated by an actual CI run
(YAML parses locally); the int-spec suite is verified passing locally on this
branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jun 26, 2026
…rrent A<->B cycles (docmost#207)

The server-side move cycle guard (getPageBreadCrumbs) and the move UPDATE ran
as two separate, unlocked statements. Two concurrent moves ("A under B" and
"B under A") could each read the same pre-write acyclic snapshot, both pass the
guard, then persist A.parentPageId=B AND B.parentPageId=A — a parent/child
cycle (TOCTOU, docmost#207 docmost#7).

Run the cycle check and the UPDATE inside one transaction (executeTx) guarded
by a per-space advisory lock (pg_advisory_xact_lock, held until COMMIT) so all
moves within a space serialize: the second mover blocks until the first commits
and then sees the freshly written parent, so its guard rejects the cycle.
getPageBreadCrumbs gains an optional trx so the check runs on the locked snapshot.

Adds an integration test driving two opposing concurrent movePage calls and
asserting no cycle ever persists and exactly one move is rejected. Updates the
movePage unit-test stubs for the new transactional path.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jun 26, 2026
…cmost#207)

getPageBreadCrumbs (ancestor CTE) and forceDelete (descendant CTE) used
withRecursive + unionAll with no CYCLE clause or depth cap. If a parent/child
cycle already exists in the data (e.g. one slipped in via the docmost#7 TOCTOU race),
both queries loop forever — hang / statement timeout. Worse, the move guard
itself runs the ancestor CTE, so a cycle would disable the very guard meant to
prevent it (docmost#207 docmost#8).

Add a depth counter bounded by MAX_PAGE_TREE_DEPTH to both recursive CTEs; the
walk stops at the cap, so a cycle yields a bounded result instead of hanging.
Real page trees are only a few levels deep, so the cap never truncates a
legitimate result. getPageBreadCrumbs selects an explicit column list (not
selectAll) so the internal depth counter never leaks into the breadcrumb shape.

Adds an integration test that seeds an A<->B cycle directly and asserts both
getPageBreadCrumbs and forceDelete return bounded / complete under a short
connection-level statement_timeout instead of hanging.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 4, 2026
…ost#293 canon docmost#7)

A `highlight` mark WITHOUT a color now serializes as the Obsidian/GFM `==text==`
syntax (closing hand-authoring gap A19); a highlight WITH a color keeps the
`<mark style="background-color: …">` HTML form (condition is deterministic on
the color attr). On the raw-HTML path (columns/spanned cells) BOTH forms stay
`<mark>` via inlineToHtml — markdown is not re-parsed inside a raw-HTML block.

Parse: `==` is not standard markdown, so the importer uses a DEDICATED marked
instance (`new Marked().use({extensions:[highlightMark]})`) rather than the
global singleton — registered once, never leaks `==` behavior to other callers.
The inline extension tokenizes `==text==` (non-empty, non-space-leading inner,
lazy so `==a== ==b==` is two marks; inner re-tokenized so nested marks survive;
`====`/`==x` fail-open to literal) into `<mark>` with no color, which the schema
parses as a color-less highlight. Inline code (`` `a == b` ``) stays code via
marked token precedence. marked 17 defaults (gfm:true, breaks:false) are
identical for the fresh instance, so tables/strike/autolinks are unaffected.

Losslessness: a LITERAL `==` in a text run would otherwise be misparsed as a
highlight on the next import, so `case "text"` backslash-escapes each `=` of a
`==` pair (marked decodes `\=` back to `=`), and this round-trips byte-stably.
The escape does NOT run for inline-code runs, and — CRITICALLY — codeBlock now
reads its child text RAW (schema `content: "text*"`) instead of routing through
`case "text"`: marked does not decode `\=` inside a fence, so escaping there
would permanently stamp backslashes into any `==` comparison (ubiquitous in
source code) and corrupt the block on the git-sync data path.

Tests: new highlight.test.ts (19 cases incl. serialize forms, colored vs plain,
column `<mark>` path, nested marks, inline-code exclusion, literal-`==` escape,
fail-open, AND a codeBlock-with-`==` regression proving no backslash corruption
+ byte-stable round-trip). Golden inline-mark matrix flipped top-level no-color
highlight to `==m==`; the kept `<mark style=…>` assertions are the colored/
raw-HTML cases.

package vitest: 559 passed; tsc clean. git-sync: 268 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 4, 2026
…cmost#6)

mathInline serializes as `$LaTeX$` and mathBlock as an own-line `$$\n<latex>\n$$`
fence (multi-line safe), closing hand-authoring gap A18. The LaTeX still lives in
node.attrs.text; a literal `$` inside it is escaped `\$`. On the raw-HTML path
(columns/cells) math keeps the schema-HTML `<span data-type="mathInline">` /
`<div data-type="mathBlock">` form (markdown is not re-parsed inside raw HTML) —
blockToHtml gets an explicit mathBlock case and inlineToHtml a mathInline case,
sharing the mathInlineHtml/mathBlockHtml helpers with the fallbacks so the two
forms cannot drift.

Parse: mathInlineExtension (inline) + mathBlockExtension (block) are added to the
SAME dedicated marked instance introduced for canon docmost#7 (global singleton
untouched). The inline extension uses a currency-safe PANDOC rule: an opening `$`
must not be followed by whitespace, and the closing `$` must not be preceded by
whitespace nor followed by a digit — so `$5`, `$5 and $10`, `a $5 b $6 c`, `100$`
stay literal text while `$x^2$` is math. The block extension matches a `$$` fence
line and captures multi-line LaTeX non-greedily up to the next `$$` line.

The pandoc boundary rule lives ONCE in the new math-inline.ts
(INLINE_MATH_SOURCE) and is shared by the import tokenizer (^-anchored) and the
export prose escaper (global), so parse and serialize cannot disagree about what
is math. escapeProseMath (case "text", non-code runs only) escapes ONLY the two
delimiting `$` of a span the rule WOULD match, so a would-be-math prose span like
`the set $A$` re-imports as literal text while currency `$5 and $10` is emitted
CLEAN (zero backslash churn). marked decodes `\$`→`$` on re-parse, byte-stable.

Fallbacks to the lossless schema-HTML form (all documented + tested):
mathInline → <span> when empty / whitespace-edged / multi-line / pre-existing
`\$` / trailing `\` / immediately before a digit-text sibling (renderInlineChildren
guard, so `$…$5` can't lose the node); mathBlock → <div> when the LaTeX contains
`$$`. Each fallback round-trips losslessly and byte-stably.

Code safety (guards the canon docmost#7 regression class): codeBlock reads raw child
text and inline `code` runs are excluded from escapeProseMath, so `$5`/`$x$` in
code stay literal with no math and no backslash corruption. ReDoS-checked on
adversarial 40k-char inputs (0–1 ms).

Tests: new math.test.ts (26 cases: serialize exactness, multi-line block, `\$`
escaping, currency ×5 asserting no `\$`, prose escape, columns schema-HTML,
inline-code/codeBlock safety, fail-open). Goldens in roundtrip / markdown-converter
flipped top-level math to `$…$`/`$$…$$`; the escapeAttr-idempotence golden wraps
math in a column (still exercises escapeAttr); columns/raw-HTML math assertions
unchanged.

package vitest: 585 passed; tsc clean. git-sync: 268 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 4, 2026
… ^ in link/alt text (docmost#333 review F5)

F1 (round 1) wrapped the image alt in escapeLinkText, and that helper also guards
the link-form media captions (attachment/pdf/embed). But its character class
covered only stock CommonMark — NOT the Docmost inline EXTENSIONS this same PR
registers on the marked instance: highlight `==x==` (canon docmost#7), math `$x$`
(canon docmost#6), footnote `^[x]` (canon #2). Their triggers `= $ ^` are not CommonMark
punctuation, so an alt or media filename like `x $A$ y`, `use ==bold==`, `^[fn]`,
or `data $A$.csv` was silently turned into a math/highlight/footnote node on
import — the same class of round-trip data loss F1 closed, reintroduced by this
PR's own canon.

Fix: add `= $ ^` to the escapeLinkText class (`/[\\`*_~[\]<&!()=$^]/g`). `\= \$ \^`
decode back to literals (all ASCII punctuation) AND, being escape tokens, stop
the extension tokenizer from matching — verified lossless byte-stable round-trip.
Updated the helper comment to name the two trigger sets (CommonMark + Docmost
inline extensions). Extended the adversarial round-trip tests: image alt gains
`x $A$ y` / `5$ and 10$` / `use ==bold==` / `^[fn]` / `cost $5 == price`; pdf name
gains `data $A$.csv` / `q3 ==final==.pdf` / `5$ and 10$.pdf` / `note ^[x].pdf` —
all byte-stable with the node intact, so the hole can't reopen.

package vitest: 658 passed; tsc clean. git-sync: 268. mcp: 454.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 12, 2026
Эскалация (владелец) — Опция A: 100k только фолбэк для НЕсконфигурированных
инсталляций; при заданном chatContextWindow бюджет = floor(0.7×window) БЕЗ капа
(бюджетер — защита от брика об контекст-окно, не эконом-лимитер). Спек репиннут
resolveReplayBudget(1_000_000)→700_000.

DO1: агрессивный next-turn recovery ×0.5 вынесен в чистую resolveEffectiveReplayThreshold
+ тест линковки replayOverflow→0.5×бюджет (mutation-verified).
DO2: checkNewComments partial-failure — per-page reject скипается (→null), скан
резолвится, порядок выживших сохранён; тест docmost#7 (mutation-verified).
DO3: ai-chat.write-volume.spec.ts → .int-spec.ts (WAL-гард не бежал НИ в одном
CI-lane) + маппер @docmost/token-estimate в jest-integration.json; реальный WAL
на pg:5432 зелёный (трейс v1 140MB→v2 0.04MB).
DO4: CHANGELOG [Unreleased] по docmost#490.
Follow-up: issue docmost#520 (эскалация агрессивной доли при незаданном окне + малом
реальном контексте).

Ребейзнут на develop (волна 1 смержена): только 6 коммитов docmost#490 над develop.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 17, 2026
…rf flake

Two failures surfaced after the round-1 e2e baseHash fix unblocked the mcp e2e:

- editor-ext table schema: TableCell/TableHeader did not declare an `align`
  attribute, so the docmost#647/docmost#672 server-side guarded-replace write path
  (jsonToNode -> Node.fromJSON(getSchema(editor-ext exts))) stripped GFM column
  alignment on persist — the old client-side collab write went through the
  converter schema (which HAS align), masking the drift. Mirror the converter's
  docmost-schema align onto editor-ext so the authoritative editor/collab schema
  preserves it (round-trips |:--|:-:|--:|). Invariants docmost#7/docmost#4.
- drawio-layout benchmark (docmost#486): the `dt < 5000` sanity bound compared TOTAL
  round-trip time (worker startup + elkjs load + IPC + the internal 5000ms ELK
  budget) against the internal budget, flaking on loaded CI runners. Loosen to
  10000ms; the event-loop-responsive / layout-applied guarantees are unchanged.

Verified: editor-ext + prosemirror-markdown build; converter tests green (895);
runtime proof that Node.fromJSON now preserves tableCell/tableHeader align;
drawio-layout perf test green locally.

Follow-up: add a CI parity check for node-attribute drift between editor-ext and
prosemirror-markdown/docmost-schema (the gap that let `align` diverge).
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 20, 2026
…d the pm-markdown mirror (docmost#684)

Invariant docmost#7 says no hand-synced mirrors: the Tiptap node schema lives in
packages/editor-ext (authoritative; collab builds getSchema(tiptapExtensions))
AND packages/prosemirror-markdown/docmost-schema.ts (the PM<->MD converter). The
existing schema-surface-snapshot.test is a LOUD MANUAL review gate, and the docmost#493
contract test is asymmetric (editor-ext -> mirror only) — so attribute drift in
the OTHER direction wasn't caught. Real incident: `align` on tableCell/tableHeader
was declared in the mirror but missing from editor-ext, so after the write path
moved to server guarded-replace (docmost#647/docmost#672), Node.fromJSON(getSchema(editor-ext))
STRIPPED align -> GFM table alignment silently lost on persist. (The align fix
itself already landed on develop; this adds the missing automatic guard.)

New apps/server/src/collaboration/schema-attr-parity.spec.ts builds BOTH schemas
and diffs the attribute-NAME set of every shared node/mark in BOTH directions,
failing with the exact node+attr+direction (e.g. "tableCell.align: in mirror but
MISSING in editor-ext (write path STRIPS it)"). A documented per-attr
ACCEPTED_DIVERGENCE allowlist covers the 4 intentional structural divergences
(highlight.colorName, image.title, youtube.align, youtube.start) — each with the
side + reason; a NEW drift is never pre-blessed, so an align-class regression
still reddens. Plus a stale-allowlist test (entries can't rot) and a vacuity
guard (>20 shared types). Lives in the server package — the only place that can
import the real write-path tiptapExtensions without a third copy of the list.

Verified: schema-attr-parity 4 passed; mirror snapshot + docmost#493 contract 6 passed;
align round-trip 34 passed. Non-vacuous: removing align from editor-ext reddens
the parity test with the exact strip message. CI runs server specs via pnpm -r
test, so it's gated with no workflow change.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 20, 2026
…ll-switch (docmost#686 phase 2)

PR A, phase 2. Adds the per-user CRUD surface a member uses to manage their OWN
external MCP servers (persisted by phase 1). Nothing consumes personal servers at
run time yet — the agent-side union + per-user cache invalidation are PR B.

- account-mcp-servers.controller.ts: @controller('account/mcp-servers'),
  JwtAuthGuard, NO CASL admin gate (a normal member manages their own). Routes
  mirror the admin controller (list/create/update/delete/test). assertEnabled()
  runs FIRST in every handler -> 403 when MCP_PERSONAL_SERVERS_ENABLED=false, so a
  disabled feature never reaches the service. Every handler passes the
  authenticated @authuser().id + @AuthWorkspace().id — no client-supplied user id.
- account-mcp-servers.service.ts: reads/writes go ONLY through phase-1 *ForUser
  repo methods (owner-scoped; a non-owner id -> 404). createPersonal in one tx:
  lockUserRow (FOR NO KEY UPDATE on the users row) -> countByUser -> >= max ->
  BadRequest -> insert, so a parallel burst can't exceed the limit. SSRF
  assertMcpUrlAllowed on create + url-change.
- mcp-server-view.util.ts: single shared definition of the write-only-headers
  projection (toMcpServerView -> hasHeaders bool, never headersEnc) + encrypt +
  url-allow, consumed by BOTH admin and personal paths (AGENTS docmost#7 — no hand-synced
  mirror of the header-leak guard). Admin service delegates to it, no behaviour
  change.
- workspace.controller entitlements now returns mcpPersonalServersEnabled (bool)
  for PR C's UI to gate the page. McpServerIdDto -> @IsUUID (a non-uuid id now 400,
  not a Postgres-22P02 500; benefits admin too).

Verified (internal adversarial review + real Postgres): no cross-user / admin<->
personal access; headers never leak in any response or log; kill-switch on every
route (neuter-proven); limit atomic (burst-of-4 with MAX=2 -> exactly 2 on real
PG); SSRF enforced. Controller unit 10/10, integration 22/22 (service 6 + repo 16),
tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 20, 2026
Add the account-level page to manage personal external MCP servers,
mirroring the account API-keys page. Extract the reusable row/form/modal
wiring into a single shared module (features/ai-mcp) consumed by both the
admin and the account pages, so the two never diverge (AGENTS invariant docmost#7).

- shared features/ai-mcp: canonical types + injectable create/update/test
  hook contract + AiMcpServerRow + McpServerFormModal + form + moved
  test-view/form-utils helpers (with their specs)
- admin ai-mcp-servers.tsx refactored to consume the shared row/modal;
  no admin behaviour change (same guard, fields, validation, confirms)
- new account page + scoped service (/account/mcp-servers*) + query key
  ["account-mcp-servers"], distinct from the admin cache
- kill-switch gating via the workspace entitlements mcpPersonalServersEnabled
  flag: friendly disabled Alert (not a raw 403), list fetch gated off,
  sidebar item hidden when off; fail-safe to hidden when the flag is absent
- routing: APP_ROUTE.SETTINGS.ACCOUNT.MCP_SERVERS + lazy route + sidebar item
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Jul 20, 2026
…fig keys were dead in dev (docmost#638)

docmost#638's finding #1 (LOCAL_FIRST_ENABLED unreachable in dev) is already fixed on
develop by 62994e1 (docmost#639), and a guard test existed. But docmost#639's guard carried a
hardcoded 4-key list — itself the hand-synced mirror invariant docmost#7 forbids — so
diffing the getConfigValue call sites against the vite allowlist found the same
defect still live for three more keys, permanently undefined in dev:

- OFFLINE_GRACE, which silently fell back to its 30d default, so docmost#640's session
  boundary could not be exercised in dev at all;
- DRAWIO_RASTER_ENABLED (docmost#629) and EXCALIDRAW_RASTER_ENABLED (docmost#632), which read
  as OFF in dev whatever .env said.

All three are mirrored into window.CONFIG by static.module.ts, so prod honoured
them — a dev-only divergence, exactly the original bug class.

Fixed by generation rather than another guard: a dependency-free
apps/client/src/lib/client-config-keys.ts exports CLIENT_CONFIG_KEYS (17 keys)
and buildDefineEnv(env), and vite.config.ts now does
`"process.env": buildDefineEnv(env)`. The destructuring block and the hand-listed
define object are both gone — three copies collapse into one (-105/+13).
Generation is clean here because the module has no imports, so Node can evaluate
it during config load and vitest can import it without the plugin graph.

It stays an explicit allowlist rather than a passthrough on purpose:
loadEnv(mode, dir, "") returns the FULL server env, so passing it through would
inline DATABASE_URL/APP_SECRET into a public bundle. A regression test pins that.
The remaining one-hop mirror (the list vs the getConfigValue call sites) is
covered by a derivation test that extracts the keys from config.ts.

The superseded flag-reachability.test.ts is deleted: its two guarantees are
strictly subsumed, since the new test covers every key through the real
generator and a key missing from either former site now surfaces identically.

Behaviour: prod is untouched (getConfigValue takes the window.CONFIG branch
there, so the process.env branch is dead code), and the three added keys are
non-secret operator flags already published to every prod browser via
window.CONFIG. LOCAL_FIRST_ENABLED still defaults to false — this makes keys
reachable, not enabled — with a test pinning all four defaults.

Both guards are neuter-proven: dropping the flag entries from CLIENT_CONFIG_KEYS
reddens the observable-property test exactly as the pre-fix bug behaved
("expected undefined to be 'true'"), and hand-listing the define object reddens
the generation test.

Scope: this is docmost#638's finding #1 only. Phases 3-7 of the local-first/offline
program are not touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Aug 2, 2026
…ntic search (docmost#696)

The picker exposed 112 curated icons of 1995; the rest were reachable only by
English substring search (≤120 hits). Now the whole catalog (1746 canonical
icons) is browsable, virtualized, and searchable by meaning (tags, aliases,
common Russian words), and the scroll-drifting Mantine name-tooltip is gone.

- lucide-catalog.generated.ts: one committed artifact (a .ts wrapper, not .json
  — a JSON import blows tsc to ~294k types), built by a hand-run generator
  (apps/client/scripts/gen-lucide-catalog.mjs; default / --tarball / --offline
  modes; NOT in CI). The dynamicIconImports parse rule is a single source
  (scripts/lucide-imports.mjs) shared by the generator and the guard test
  (AGENTS.md docmost#7). The artifact carries degraded[]; the guard test fails the
  build on a non-empty degraded not matching the allowlist (AGENTS.md docmost#10).
- lucide-icon-grid.tsx: @tanstack/react-virtual grid (explicit measure() so the
  estimateSize-not-in-deps memo recomputes on model switch), a loading|ready|
  failed state machine, imperative dynamic import of the catalog (never eager,
  and a reject can't burn the app-wide reload budget), visible failed+limited
  mode with Retry. Tooltip removed. The catalog is reachable ONLY via the one
  dynamic import (image-smoke.sh S5 guards against it entering an eager chunk).
- lucide-search.ts: canonicalOf (a stored alias still resolves), tag/alias
  search, Russian synonyms (ru-icon-synonyms.ts). Curated aliases funnel /
  circle-question-mark canonicalized.
- i18n: picker strings in en-US (source) + ru-RU.

Verified: pnpm --filter client typecheck / build / test all pass (1748 tests,
incl. the guard + search suites against the real catalog); generator idempotent
and byte-identical across all three network modes; degraded-guard mutation-checked
non-vacuous.
vvzvlad pushed a commit to vvzvlad/gitmost that referenced this pull request Aug 2, 2026
…ost#681)

editor_tx_ms only measures the synchronous ProseMirror transaction and is
blind to the React re-render/paint that follows a keystroke. Add
editor_key_latency_ms = the full keydown->next-paint duration, captured with a
PerformanceObserver on the Event Timing entry type.

- vitals.ts: a passive PerformanceObserver({type:"event", durationThreshold:16,
  buffered:false}), wrapped in the same commented try/catch graceful-degrade as
  the longtask observer (inv docmost#10). A single filter reportEditorKeyLatency() keeps
  name==="keydown" && live .ProseMirror focus, reporting entry.duration through
  the existing reportClientMetric sink (self-gated by isVitalsActive). editor_tx_ms
  is untouched.
- client ALLOWED_NAMES + reportClientMetric union + server ALLOWED_METRIC_NAMES:
  the metric name declared in all three lockstep sites (inv docmost#7), each guarded.
- 6 client tests drive the REAL filter through real jsdom focus + a wiring test
  proving the observer is installed with the right options and connected to the
  filter (inv docmost#8); 1 server validator test asserts the name is accepted.

Closes docmost#681
PMQ9 referenced this pull request in cccvu/docmost Aug 30, 2026
…iction gate, collab guard, PEP↔PDP consumer contract; timeout/chunking bug-exposers (#10/#15)

Adds to the fork's authz compatibility suite (Task #16 / Epic #7):
- search/pdp-search.completeness.spec.ts — I5 authorized-k-under-truncation (match beyond the first window still returned)
- page-restriction/page-restriction.service.spec.ts — only a space-admin may restrict/grant
- collab/collab-disconnect.controller.spec.ts — constant-time service-secret guard + fail-safe re-check
- contract/pep-pdp-consumer.contract.spec.ts — the consumer half of the PEP↔PDP HTTP contract (#13)
- platform-authz.client.contract.spec.ts — INTENTIONALLY-RED bug-exposers: no request timeout (#10), no 1000-cap chunking (#15)

All additions live under apps/server/src/authz/ (upstream boundary intact). The two client-contract
failures are real defects to fix in #17; they assert intended behavior and are not weakened.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant