NEC-117: Run the directory’s own rules on every commit, and cut 0.2.1 #177

Merged
nectenda-agent merged 2 commits from worktree-nec-117-run-the-directory-lint-rules-on-every-co into main 2026-09-25 21:32:56 +01:00
Collaborator

Task: NEC-117 — https://projectron.nerchure.com/tasks/117

The community directory rejected 0.2.0, and we found out by reading a web page —
the sixth version reviewed that way. The rules it runs are published packages,
so they now run here on every commit.

Layer 1, pnpm lint: eslint-plugin-obsidianmd scoped to the two packages
that get published, and stylelint-config-obsidianmd over the one stylesheet
that ships. Nothing linted CSS here before.

Calibrated before trusted. Against 0.2.0's scorecard the gate reports the
same findings at the same lines — the no-static-styles-assignment error at
remote-pointer.ts:535,537, four fetch, five prefer-create-el, two
Vault.trash, two globalThis, and the three CSS findings at
styles.css:635,832,849. Fifteen and three, matching theirs. Mutation-checked
both ways: a fresh el.style.color is an error, a fresh createElement,
!important and :has are warnings.

Two ways it would have looked clean while doing nothing, both closed. The
plugin reads manifest.json with a CWD-relative readFileSync and caches null
on failure, so from the repo root six rules silently no-op and a stack trace
prints every run — a root symlink fixes both, and minAppVersion is passed
explicitly as well. And stylelint-config-obsidianmd targets electron >= 43
while the directory reported against Obsidian 1.11.4 = Electron 39, so the
default would have missed clip-path entirely.

The upstream stylelint config extends stylelint-config-standard, which brought
82 findings about blank lines and #ffffff over #fff. This repository runs no
formatter by decision, and 82 findings nobody intends to fix is the gate people
learn to skip. Those rules are off; the extension is kept so a new Obsidian
rule still arrives on its own.

Fixed: the blocker (setCssStyles, byte-equivalent — the animation was
always in the stylesheet), and createDiv/createSpan/createSvg in place of
document.createElement. The SVG pair was nearly suppressed as a rule
limitation; createSvg is a separate helper typed over SVGElementTagNameMap
and does exactly this. The rule was right.

Refused, each suppressed next-line with the reason: Vault.trash twice —
trashFile() honours a preference whose branches include permanent deletion, on
paths driven by another vault's delete; globalThis twice, in a package that
also runs under Node where window throws; fetch three times, for streaming,
for abort, and for a bucket that must see no preflight; and the three CSS
findings. docs/releasing.md carries the table and the arguments.

BUILD VERIFICATION — the warning is not ours. The Version review said for
0.1.4 and 0.1.5 that the build output does not match the released main.js.
Checked rather than accepted: a clean clone of the mirror at tag 0.1.5,
pnpm install && pnpm build, gives a main.js whose sha256 equals both the
release-notes digest and the uploaded asset. All three identical. So
docs/positioning.md was left alone — weakening a claim that reproduces on
demand would be the wrong correction. Their harness almost certainly uses npm:
esbuild records each module's resolved path in the bundle, and those encode the
package manager's layout. The release notes now say so and ask for
--frozen-lockfile.

0.2.1 carries nothing a user can see, and its changelog entry says so rather
than dressing the lint fix up as a bug fix. It points anyone on 0.1.5 at 0.2.0's
notes, where the two edit-loss fixes are.

Not yet done, and deliberately before any tag: the official Review branch
check against this branch. That is the one thing we cannot run ourselves, and a
tag is not reusable.

Changelog

NONE

Task: NEC-117 — https://projectron.nerchure.com/tasks/117 The community directory rejected 0.2.0, and we found out by reading a web page — the sixth version reviewed that way. The rules it runs are published packages, so they now run here on every commit. **Layer 1, `pnpm lint`:** `eslint-plugin-obsidianmd` scoped to the two packages that get published, and `stylelint-config-obsidianmd` over the one stylesheet that ships. Nothing linted CSS here before. **Calibrated before trusted.** Against 0.2.0's scorecard the gate reports the same findings at the same lines — the `no-static-styles-assignment` error at `remote-pointer.ts:535,537`, four `fetch`, five `prefer-create-el`, two `Vault.trash`, two `globalThis`, and the three CSS findings at `styles.css:635,832,849`. Fifteen and three, matching theirs. Mutation-checked both ways: a fresh `el.style.color` is an error, a fresh `createElement`, `!important` and `:has` are warnings. **Two ways it would have looked clean while doing nothing, both closed.** The plugin reads `manifest.json` with a CWD-relative `readFileSync` and caches null on failure, so from the repo root six rules silently no-op and a stack trace prints every run — a root symlink fixes both, and `minAppVersion` is passed explicitly as well. And `stylelint-config-obsidianmd` targets `electron >= 43` while the directory reported against Obsidian 1.11.4 = Electron 39, so the default would have missed `clip-path` entirely. The upstream stylelint config extends `stylelint-config-standard`, which brought 82 findings about blank lines and `#ffffff` over `#fff`. This repository runs no formatter by decision, and 82 findings nobody intends to fix is the gate people learn to skip. Those rules are off; the extension is kept so a new *Obsidian* rule still arrives on its own. **Fixed:** the blocker (`setCssStyles`, byte-equivalent — the animation was always in the stylesheet), and `createDiv`/`createSpan`/`createSvg` in place of `document.createElement`. The SVG pair was nearly suppressed as a rule limitation; `createSvg` is a separate helper typed over `SVGElementTagNameMap` and does exactly this. The rule was right. **Refused, each suppressed next-line with the reason:** `Vault.trash` twice — `trashFile()` honours a preference whose branches include permanent deletion, on paths driven by *another vault's* delete; `globalThis` twice, in a package that also runs under Node where `window` throws; `fetch` three times, for streaming, for abort, and for a bucket that must see no preflight; and the three CSS findings. `docs/releasing.md` carries the table and the arguments. **BUILD VERIFICATION — the warning is not ours.** The Version review said for 0.1.4 and 0.1.5 that the build output does not match the released `main.js`. Checked rather than accepted: a clean clone of the mirror at tag `0.1.5`, `pnpm install && pnpm build`, gives a `main.js` whose sha256 equals both the release-notes digest and the uploaded asset. All three identical. So `docs/positioning.md` was left alone — weakening a claim that reproduces on demand would be the wrong correction. Their harness almost certainly uses npm: esbuild records each module's resolved path in the bundle, and those encode the package manager's layout. The release notes now say so and ask for `--frozen-lockfile`. **0.2.1 carries nothing a user can see**, and its changelog entry says so rather than dressing the lint fix up as a bug fix. It points anyone on 0.1.5 at 0.2.0's notes, where the two edit-loss fixes are. **Not yet done, and deliberately before any tag:** the official *Review branch* check against this branch. That is the one thing we cannot run ourselves, and a tag is not reusable. ## Changelog NONE
0.2.0 was rejected by the directory's automated review over two lines, and we
found out by reading a web page afterwards. Six versions have been reviewed that
way. The rules it runs are published packages, so this runs them on every
commit: `eslint-plugin-obsidianmd` over the two packages that get published, and
`stylelint-config-obsidianmd` over the one stylesheet that ships. Nothing here
linted CSS at all before.

Calibrated before it was trusted, because a gate that cannot produce a failure
we already know about proves nothing. Against the scorecard for 0.2.0 it now
reports the same findings at the same sites: the `no-static-styles-assignment`
error at remote-pointer 535 and 537, the four `fetch` sites, five
`prefer-create-el`, two `Vault.trash`, two `globalThis`, and the three CSS
findings at styles.css 635, 832 and 849. Fifteen and three, matching theirs.

Two ways it would have looked clean while doing nothing, both now closed:

- The plugin reads `manifest.json` with a CWD-relative `readFileSync` and caches
  null on failure, so from the repository root six rules silently no-op and a
  stack trace prints on every run. The mirror does not hit this because
  `build-mirror.mjs` puts a manifest at its root, which is where the directory
  reads it from. A root symlink does the same here, and `minAppVersion` is
  passed explicitly as well, read from the manifest so it cannot go stale.
- `stylelint-config-obsidianmd` targets `electron >= 43`; the directory reported
  against Obsidian 1.11.4, which is Electron 39. The default would have missed
  the `clip-path` finding entirely. Pinned to 39, with a note that this tracks
  their floor and not our `minAppVersion`.

The upstream stylelint config extends `stylelint-config-standard`, which brought
82 findings about blank lines and `#ffffff` over `#fff`. None are what the
directory reports and none say anything is wrong, and this repository runs no
formatter by decision. Those rules are off; the extension is kept so a new
*Obsidian* rule still arrives on its own.

What the rules found, fixed:

- **The blocker.** `timeFade` wrote `el.style.animation` twice with literal
  values. `setCssStyles` is a thin wrapper over the same property, so the
  behaviour is identical; the animation was always in the stylesheet. A class
  toggle would read better and cannot be used — the delay has to be set in the
  same frame as the restart, and the reflow between the writes is what makes the
  browser notice.
- `createDiv`/`createSpan`/`createSvg` in place of `document.createElement`. The
  SVG pair was nearly suppressed as a rule limitation: `createEl` cannot type a
  namespaced element, but `createSvg` is a separate helper typed over
  SVGElementTagNameMap and does exactly this. The rule was right and the earlier
  reading of it was wrong.
- `file-status-indicator` now uses `createSpan` like `header-status` already
  did. The only reason it did not was that `querySelectorAll` yields `Element`,
  so the helper was not in scope; the fix is the type argument. Its test fake
  grew a `createSpan` to match.

What they found and we refuse, each suppressed next-line with the reason beside
the comment that already argued it: `Vault.trash` twice, because `trashFile()`
honours a preference whose branches include permanent deletion on a path driven
by another vault's delete; `globalThis` twice, because that package also runs
under Node where `window` throws rather than reporting absence; `fetch` three
times, for streaming, for abort, and for a bucket that must see no preflight;
and the three CSS findings. A narrow suppression with a reason keeps the
baseline quiet, so the next real violation is visible rather than the sixteenth
line of a report nobody reads.

Mutation-checked in both directions: a fresh `el.style.color` is an error, a
fresh `createElement`, `!important` and `:has` are warnings, and the tree is
clean without them.

Task: NEC-117

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5JXvAgQ27c4dMFDB2Wu5B
Cut 0.2.1, and record what the directory checks and what we refuse
All checks were successful
Release note / release-note (pull_request) Successful in 14s
CI / build (pull_request) Successful in 4m41s
CI / e2e (pull_request) Successful in 5m21s
CI / promote (pull_request) Has been skipped
Deploy site / deploy (push) Successful in 53s
CI / build (push) Successful in 4m55s
CI / e2e (push) Successful in 5m16s
CI / promote (push) Successful in 32s
c7987f1d94
0.2.1 carries nothing a user can see. It exists so that 0.2.0's contents can
reach the community directory, which rejected 0.2.0 over how one animation is
applied rather than over anything the plugin does. The changelog says exactly
that, and points anyone coming from 0.1.5 at 0.2.0's notes, where the two
edit-loss fixes are. Writing it as "fixed the pointer fade" would have been
untrue: `setCssStyles` sets the same property the same way.

`docs/releasing.md` gains the contract, which `plan-phase10.md` always said
belonged there rather than in a phase document: which of the directory's checks
now run here, that they were calibrated against 0.2.0's scorecard before being
trusted, and a table of what it reports that we do not intend to fix, each with
the argument. The head of `eslint.config.mjs` already told the first half of
this story — twenty findings neither lint nor typecheck could see — so it now
points at the second half rather than repeating it.

**BUILD VERIFICATION: the warning is not ours.** The Version review told us for
both 0.1.4 and 0.1.5 that the build output does not match the released
`main.js`. Checked rather than accepted: a clean clone of the mirror at tag
0.1.5, `pnpm install && pnpm build`, produces a `main.js` whose sha256 equals
both the digest in the release notes and the uploaded asset. All three
identical. The claim in `docs/security-model.md` stands as written, and
`docs/positioning.md` was left alone — weakening a claim that reproduces on
demand would have been the wrong correction.

What their harness almost certainly hits instead: esbuild records each module's
resolved path in the bundle as a comment, and those paths encode the package
manager's layout, so npm yields a byte-different bundle that behaves
identically. The release notes now say so, and the recipe asks for
`--frozen-lockfile`. `packageManager` already pins pnpm and corepack honours it,
which is why the recipe reproduced without the verifier being told anything.

Task: NEC-117

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5JXvAgQ27c4dMFDB2Wu5B
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Nectenda/nectenda!177
No description provided.