HF-359: Turn the repository into a monorepo, and rebuild the developer documentation - #1757
Draft
sequba wants to merge 28 commits into
Draft
HF-359: Turn the repository into a monorepo, and rebuild the developer documentation#1757sequba wants to merge 28 commits into
sequba wants to merge 28 commits into
Conversation
…ions Prepare the repository for the monorepo migration by moving from a single always-loaded DEV_DOCS.md to a three-layer documentation model: - AGENTS.md files carry the rules that must not be violated, plus a map of where to look next. They load automatically in their own subtree, so an agent working in src/parser/ never pays for the docs portal's conventions. Added at the repository root and in src/, src/parser/, src/interpreter/, src/interpreter/plugin/, src/interpreter/functionMetadata/, src/DependencyGraph/, src/i18n/, docs/, test/, and script/. In each directory CLAUDE.md is a symlink to the sibling AGENTS.md, so Claude Code and Cursor read one source. - dev-docs/ holds the deep reference, loaded on demand: structure, architecture, build, testing, definition of done, code style, doc standards, the function metadata catalogue, i18n, worktrees, and the target monorepo layout. DEV_DOCS.md becomes an index into it. - .claude/skills/ holds nine task workflows, scoped with the paths frontmatter field so each loads only where it applies. Also add .worktreeinclude, which documents why the branch-matched private test suite and the build outputs must never be copied into a worktree, and .claude/settings.json, which enables the typescript-lsp plugin repository-wide and keeps build output out of agent reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The build outputs are git-ignored, so content searches already skip them, but nothing stopped an agent from opening dist/hyperformula.js directly or from answering a question about behaviour from typings/ instead of src/. Extend the deny list to cover every generated path in .gitignore. node_modules/ and package-lock.json are deliberately left readable: reading a dependency's source is occasionally the right move when debugging, and a deny rule would also block a targeted grep for a dependency version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add script/claude/post-tool-use.mjs, wired to Edit|Write in .claude/settings.json. Claude Code passes the tool payload as JSON on stdin; the hook reads the edited path, lints that one file, and exits 2 with whatever errors remain, which Claude Code shows to the agent. ESLint rules are then applied while the change is still being written, instead of surfacing when someone runs `npm run lint` at the end. Three properties are deliberate: - It reports errors only. `npm run lint` produces tens of thousands of warnings across the repository, and reporting them per edit would bury the agent in noise unrelated to its own change. - It passes `--fix-type problem,layout`, never a plain `--fix`. The jsdoc/require-jsdoc rule autofixes by inserting an EMPTY JSDoc block above every undocumented declaration, so an unconstrained --fix quietly scatters those stubs through any file the agent touches. The restriction keeps the fixes that are unambiguously right (licence header, semicolons, quotes, spacing) and leaves the rest to a human. - It fails open. A missing binary, a spawn failure, or unparseable output exits 0 in silence, because a broken hook must never block work. Also record three decisions in the monorepo plan: the repository stays on npm workspaces, the private test suite stays branch-matched and only changes its checkout path, and every package versions and releases on its own cadence, which implies a changelog per package and package-namespaced tags. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every package versions and releases on its own cadence, so the plan now calls for a changelog per package, package-namespaced tags, and a release workflow parameterised by package. The UI packages keep the @hfe/* scope they publish under today: moving them between repositories is enough change for one migration, and a rename would break every existing consumer's imports for no benefit the move itself delivers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
Task linked: HF-359 Add formula builder core to hyperformula repo |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
hyperformula-docs | b562c14 | Sep 01 2026, 01:58 PM |
Four decisions, and one small correctness fix on the way through. - The Cloudflare Worker moves under docs/. wrangler.jsonc and worker/index.js exist only to serve the built portal, and docs/ is leaving the workspace anyway, so the whole deployment path ends up in one directory that installs and versions on its own. - CHANGELOG.md keeps its current Keep a Changelog form, one file per package. No JSON fragment mechanism: fragments solve merge conflicts this repository does not have yet, and introducing them now would cost a tool, a CI gate, and a habit change for a problem that has not arrived. - Every .nvmrc says 22, docs/ included once it has its own. - The HyperFormula agent skill stays in the shared skills repository for now. Bringing it here is a separate decision taken later; nothing in the target layout depends on it. The fix: test/fetch-tests.sh called the directory holding test/ REPO_ROOT. That is true today and stops being true once src/ and test/ move a level down, where it becomes the package root instead. Renamed to PACKAGE_ROOT, with a comment recording that all the script needs from it is a path inside the git working tree, which `git rev-parse` resolves from any depth. Verified by running `npm run test:setup-private`. Also correct docs/README.md, which still asked for Node.js 14+. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Performance comparison of head (0c2372b) vs base (286a731) |
sequba
commented
Aug 31, 2026
Review feedback. Every AGENTS.md, README.md, and SKILL.md now carries only what is so specific to its own context that it would be useless anywhere else; everything else is a link into dev-docs/. Two copies of a rule means one of them is wrong within a release, and the reader cannot tell which. The always-loaded surface drops from roughly 610 lines to 147. Each per-directory AGENTS.md is now six to ten lines: what the directory is, and where the detail lives. The nine skills drop from about 1100 lines to 273, keeping the steps and the ordering and linking out for the rules those steps enforce. The content moved rather than being deleted. dev-docs/ gains PARSER.md, INTERPRETER.md (including the whole built-in function contract), DEPENDENCY-GRAPH.md, PULL-REQUESTS.md, and AGENT-TOOLING.md; ARCHITECTURE.md absorbs the engine-wide invariants, TESTING.md the test-writing discipline, DOC-STANDARDS.md the changelog rules, FUNCTION-CATALOGUE.md and I18N.md their respective rule lists. script/README.md is new, and docs/README.md now describes the portal it lives in. BUILD.md no longer restates docs/guide/building.md. It points there for the command reference and keeps only what the public guide omits: the intermediate lib/ build, HF_COMPILE, the generated documentation and the rule against committing it, the Cloudflare deployment path, release, and packaging. MONOREPO.md drops the rationale and open-questions sections - the plan is worth keeping, the record of how it was decided is not - and hyperformula-ui is now one package rather than four. DEV_DOCS.md is removed; dev-docs/README.md is the front page. .ai/ exists with a single sentence pointing at dev-docs/, for agents that look there by habit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Step 1 of all nine skills is now "Read the relevant files from dev-docs/", followed by a table of the specific pages with one line each saying why that page matters for this task. Not a general pointer at the directory: a skill that says "the rules live in dev-docs" invites an agent to skip the reading and work from the steps alone, which is exactly the failure the single-source rule exists to prevent. Where the reference depends on what the task touches, the step lists the always-read pages first and the conditional ones after: hyperformula-dev reads ARCHITECTURE, CODE-STYLE, and DEFINITION-OF-DONE every time, then the page for the pipeline stage being changed. hyperformula-code-review does the same with DEFINITION-OF-DONE, CODE-STYLE, and TESTING. The remaining steps are renumbered to follow, so every skill now reads as one ordered procedure rather than a set of sections. The convention is recorded in dev-docs/AGENT-TOOLING.md, so the next skill written here inherits it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DOCS_CONTENT_GUIDE.md was the last piece of internal documentation sitting outside dev-docs/, which made it the one page an agent had to be told about separately. It is now dev-docs/DOCS-CONTENT-GUIDE.md, named like its neighbours, and reachable from the index like everything else. The move exposed a contradiction between it and DOC-STANDARDS.md, which is exactly what having two homes for documentation rules produces. DOC-STANDARDS said a guide should not duplicate the API reference; the content guide says self-containment beats DRY and a page must let the reader finish the task without leaving it. Both are right about different things, so the two pages now divide the work explicitly: DOC-STANDARDS covers WHEN documentation is required and what must be true of it, DOCS-CONTENT-GUIDE covers HOW to write the page, and DOC-STANDARDS defers to it on the trade-off rather than restating a competing rule. Also repair four checklist items whose continuation lines were wrapped in stray code fences, so half of each item rendered as a code block. References updated in docs/AGENTS.md, docs/README.md, dev-docs/README.md, dev-docs/STRUCTURE.md, and the writing-docs-pages skill, where the guide moves into the step-1 reading table and the self-review checklist becomes an explicit closing step. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 tasks
sequba
commented
Sep 1, 2026
sequba
commented
Sep 1, 2026
Four threads. The routing table has moved out of AGENTS.md and into dev-docs/README.md, where it replaces the categorised file listing rather than sitting beside it. The two were the same information keyed differently, which is exactly the duplication the single-source rule exists to prevent. AGENTS.md now points at that page and says nothing about which page covers what. The line describing how directory AGENTS.md files load is folded into the same sentence rather than trailing the table it belonged to. STRUCTURE.md and MONOREPO.md are one file. Both carried a tree, and a reader had to know which of the two was current; STRUCTURE.md now covers where everything lives today and, under "Where it is going", the packages, the target tree and the migration steps. MONOREPO.md is deleted and its two inbound links repointed. The changelog skill states the link rule where it is acted on. It said only that "the entry carries a link" at the step where the number is obtained, and named the issue-or-pull-request rule two steps later; both steps now say it outright. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sequba
force-pushed
the
feat/hf-359-monorepo-agent-setup
branch
from
September 1, 2026 08:17
efb7b74 to
80753c5
Compare
Removes script/claude/post-tool-use.mjs, its registration in .claude/settings.json, and the documentation that described it. The hook linted every file an agent wrote and reported the remaining errors back. It worked, but it is not something this repository has to carry: lint still runs from the root on demand and in CI, which is where the gate belongs, and the hook added a per-edit cost and a file that rewrote the agent's work underneath it for a convenience nobody asked for. script/ now holds only build, docs-generation and release scripts, and dev-docs/AGENT-TOOLING.md covers settings and skills alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An audit traced the deleted DEV_DOCS.md and the rewritten AGENTS.md into the new tree. Almost nothing was lost; what it found instead were assertions the new prose invented, and rules that the split left in two places where they promptly disagreed. dev-docs/README.md told the reader to write repository-root-relative link targets. The directory it governs contains 33 filesystem-relative link targets and not one root-relative one, eight of them in that same file. An agent obeying the stated rule writes links that resolve nowhere. The convention now describes what the tree does, separating link targets from paths named in prose. "The five places a function change must touch" was a term of art naming two different sets: INTERPRETER.md omitted the changelog and counted implementedFunctions separately, while the review skill counted the changelog and folded implementedFunctions in. INTERPRETER.md is now the one list, and both skills link to it instead of keeping a copy. The same list claimed all five omissions fail silently, contradicting FUNCTION-CATALOGUE.md, which correctly says a missing catalogue entry fails the docs build; it now states the split. Two file locations were wrong in the same way, and last round only one of them was fixed: absolutizeDependencies.ts and LazilyTransformingAstService.ts both live at the source root, not in the parser and dependency-graph directories that listed them. I18N.md claimed argument separators are part of the language package. They are configuration - functionArgSeparator and its neighbours in ConfigParams - and nothing in the old documentation said otherwise. Also corrected: test:compatibility pointed at a path in neither repository, and the prose blamed its failure on a missing private suite; BUILD.md presented the portal deploy commands with no hint that deployment is automated on push and these exist for debugging; AGENT-TOOLING.md said every skill is scoped by a paths glob, when four of nine deliberately are not; STRUCTURE.md inventoried a .claude/agents/ that does not exist; the root AGENTS.md stated the single-source rule without the "only what is specific to its own context" exception its two other copies carry; CODE-STYLE.md claimed everything below its intro is what ESLint cannot check, while the jsdoc rules are enabled, and had lost the deep link to the linter section of the building guide; docs/README.md still said npm install where BUILD.md says npm ci; and DOC-STANDARDS.md stated the changelog exemption twice with two different scopes. The rule that agents should read the repository's own documentation rather than fetching the rendered portal was dropped in the rewrite and is restored. Two findings were not acted on: one describes the PostToolUse hook, which has since been deleted, and one is a wording quibble about which generator runs first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The monorepo plan had every package versioning and releasing on its own cadence, each with its own CHANGELOG.md. Reversed: the packages release together on one version, and CHANGELOG.md at the repository root stays the single history for all of them. One number to reason about — the version a user reports a bug against identifies the state of the whole repository — at the cost of publishing a package whose code did not move. Entries name the package they concern when the text does not make it obvious. The published package still needs a changelog inside its tarball, so the plan now says the release copies the root file in at pack time rather than keeping a second copy under version control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
### Context Turns the repository into a monorepo (HF-359). Stacked on #1757 — review that first; this branch is based on it. ``` hyperformula/ the engine (published) — src/, test/, its toolchain, dev-docs/, script/ hyperformula-ui/ placeholder — imported separately, preserving history docs/ the portal — own package, installs on its own script/ release/ and the licence gate — the only things that span the repo dev-docs/ repository-wide standards and process ``` **No changes under `hyperformula/src/`.** Every file there is byte-identical to its old location at `src/`; git records the moves as renames throughout, so history follows. The published package's name, version, entry points and `exports` map are unchanged, and the publish-package check confirms the tarball contents. #### The layout - **npm workspaces**, with a private root carrying fan-out scripts. **Linting is the deliberate exception** — it runs once from the root over the whole repository, so nothing between packages falls through the gap. - **`docs/` left the workspace.** VuePress 1.x and its `--openssl-legacy-provider` tree must never reach an engine install. `npm run docs:install` installs it; CI installs it before building the portal. - **The portal owns its deployment**: `wrangler.jsonc` and `worker/` moved under `docs/`, and the asset directory is now `./.vuepress/dist`. - **Per-package `.nvmrc` (all `22`) and `CHANGELOG.md`.** The root changelog is an index. - **The private test suite stays branch-matched**; only its checkout path moved. Its 501 spec files needed no edit — they import the engine relatively, and because `src/` and `test/` moved *together*, the depth from a spec to the package root is unchanged. #### Everything lives with what owns it Three directories are scoped by ownership rather than by convenience: | | Repository level | Package level | |---|---|---| | `dev-docs/` | definition of done, code style, testing standards, documentation rules, build and release, pull requests, worktrees, agent setup | `hyperformula/dev-docs/` — architecture, parser, interpreter, dependency graph, function catalogue, i18n, performance, its suites, its build | | `script/` | `release/`, `check-licenses.mjs` | `hyperformula/script/` — build checks; `docs/script/` — the generator and the Worker assets | Three `dev-docs` pages were genuinely mixed and were **split, not moved**: `TESTING` (what a change must prove stays; the suites go down), `BUILD` (workspace, deploy and release stay; the intermediate build and packaging go down), and `CODE-STYLE` (style stays; the hot-path table becomes `hyperformula/dev-docs/PERFORMANCE.md`). The `script/` split was decided by the callers, not by judgement: every script had exactly one caller and the caller named its owner. Twelve call sites stopped reaching across a package boundary, and two scripts stopped resolving paths through `../hyperformula/` and `../docs/`. `examples/` at the root is **deleted**: three of its PNGs were byte-identical duplicates of `docs/.vuepress/public/`, which is what the site actually serves through `$withBase`, and the remaining five files were referenced nowhere in the repository, in any config, workflow or package script. It was not moved under `docs/`, because `docs/examples/` already exists and is a different thing — the runnable examples embedded in the guides. #### Three things the move forced **The built-in-functions generator moved to `docs/script/`.** It uses `@vuepress/shared-utils` for slugs, and that package `require()`s `markdown-it-emoji` and an older `lru-cache` major without declaring either, relying on `vuepress` to hoist them. Outside a full VuePress tree it throws on load, so the generator has to live where that tree is. **Three portal paths were written from the old repository root** and broke once VuePress built from `docs/` as its own package. All three are now anchored explicitly rather than depending on cwd: | Path | Was | Symptom | |---|---|---| | code-snippet plugin | fell back to `process.cwd()` | looked in `docs/docs/examples/`, so every guide with an example failed to compile | | `graph.vue` | `../../../dist/hyperformula.full` | resolved to a `dist/` at the repository root that no longer exists | | `dest` | `docs/.vuepress/dist/docs` | wrote the built site to `docs/docs/` | **One decision was reversed.** The migration first kept a single `dev-docs/`, on the grounds that two directories would fragment the single source of truth. That conflated a *single source* with a *single directory* — splitting by ownership is not duplication, and keeping every package's internals at the root would have made the root a dumping ground the moment `hyperformula-ui` lands. ### How did you test your changes? Everything below was run on this branch, and re-run after each review round: | Command | Result | |---|---| | `npm ci` | Clean; the workspace resolves | | `npm run lint` | **0 errors**, 54011 warnings — the exact pre-move baseline | | `npm run test:jest` | **502 suites, 6232 passed**, 3 skipped, 0 failed | | `npm run test:ci -- --coverage` | **97.42%**, written to `hyperformula/coverage` | | `npm run test:browser` | **12464 assertions** across headless Chrome and Firefox, exit 0 | | `npm run verify:typings` | `tsc --noEmit` clean | | `npm run bundle-all` | Every bundle, and **`Publish package check: OK`** | | `npm run check:licenses` | 3 production dependencies, and it **fails** when the allowlist is tightened | | `npm run release code-freeze …` (dry run) | Correct version, dates and changelog section | | `npm run docs:generate-function-docs` | 564 lines, 13 category sections | | `npm --prefix docs run build:cf` | **217 pages**, sitemap, `_headers` and `_redirects` copied | Every relative Markdown link in `AGENTS.md`, both `dev-docs/` levels, the skills and the READMEs was resolved against the filesystem. All resolve. ### What the reviews found Two rounds, 36 findings. What they caught, and what I got wrong: **Flag arguments were being swallowed.** Every delegating root script dropped them, so `npm run test:ci -- --coverage` reached the workspace as bare `jest --forceExit` and CI had been uploading nothing to Codecov while staying green. Demonstrated with `npm run test:jest -- --listTests`, which ran all 6232 tests instead of listing them. **The release script was never adapted.** It read and wrote `./package.json` — now the private root manifest with no `version`. A dry run then surfaced a third path the review had not caught: the private test suite check still pointed at `test/hyperformula-tests`. **`check:licenses` was checking nothing.** `license-checker` reads `<start>/node_modules`, and npm hoists, so from `hyperformula/` it saw an empty tree and from the root a manifest with no dependencies — zero packages either way, passing vacuously. Replaced with `script/check-licenses.mjs`, which takes the production set from `npm ls --omit=dev` and the licence data from a full `license-checker` run. **The portal was publishing internal agent instructions.** VuePress sweeps every markdown file under `docs/`, so `docs/AGENTS.md` and its `CLAUDE.md` symlink were being built into `/AGENTS.html` and a byte-identical `/CLAUDE.html`, sitemap and search index included. **`npm run test:browser` exited non-zero after passing.** All 6232 specs passed in both browsers and always did; Chrome then reported `Some of your tests did a full page reload!` during teardown. The cause was `client.clearContext: false` in the Karma base config, and it reproduced on the pre-move tree, so it predates this branch. `concurrency: 1` was the obvious first guess and is wrong — it does not remove the error, it doubles wall-clock time, and it turns an intermittent failure into a deterministic one. That determinism was what made the real fix testable, but it is not in the branch. Also fixed: the report path in `performance.yml`, `code-examples-generator.sh` silently processing zero files, `docs:build:cf` never installing the portal, `docs:install` using `npm install` against a committed lockfile, the licence-header rule resolving against the working directory, `hyperformula-ui` listed as a workspace with no `package.json`, a dead `.npmignore` rule, and a dozen documentation pages naming commands or paths this branch had moved. One finding was **rejected on measurement** — the claim that the type-aware ESLint program build risks the hook's 60 s timeout. Measured at **0.91 s** and **1.06 s**. That hook has since been removed anyway, at the author's request. ### Two pre-existing problems the move uncovered **The source language packs have never been linted.** The old ignore list carried a bare `languages` entry meant for the build output. An unanchored pattern matches a directory of that name at *any* depth, so it also excluded `src/i18n/languages/` — and the `sort-keys` override that targets exactly those files has therefore never run. Anchoring the build-output entry exposed **881 violations** across 19 translation files. Confirmed by linting `src/i18n/languages/enGB.ts` on the pre-move tree, where ESLint reports `File ignored because of a matching ignore pattern`. They are excluded again in `.eslintignore`, deliberately and with a comment explaining why, so this branch keeps the exact pre-move lint baseline. Sorting 19 translation files is a change of its own — and reordering translation entries is precisely what the i18n guidance tells contributors not to do casually. **`npm run test:compatibility` was broken for everyone.** It pointed at `test/compatibility/test-compatibility.sh`, a path that exists in neither repository; the script ships with the private suite. Fixed here, since the branch had to touch it anyway. ### Not done, on purpose **CI path filtering.** A naive `paths:` filter on a workflow that branch protection lists as a required check leaves the check permanently pending and makes pull requests unmergeable. Doing it safely needs the required-checks list, which lives in repository settings rather than in the tree, plus the `dorny/paths-filter`-and-single-gate shape the Handsontable monorepo uses. The CI *paths* are all updated; only the filtering is outstanding. **Importing `hyperformula-ui`** — left for the teammate who owns the formula-builder move. It is deliberately *not* in the `workspaces` array until then: npm silently ignores an entry with no `package.json`, and the lockfile has to be regenerated when the real package lands either way. Both are recorded in `dev-docs/STRUCTURE.md` under "What the move still owes". ### Types of changes - [ ] Breaking change (a fix or a feature because of which an existing functionality doesn't work as expected anymore) - [x] New feature or improvement (a non-breaking change that adds functionality) - [ ] Bug fix (a non-breaking change that fixes an issue) - [ ] Additional language file, or a change to an existing language file (translations) - [x] Change to the documentation Repository structure and tooling. The published package is unchanged. ### Related issues: 1. HF-359 ### Checklist: - [x] I have reviewed the guidelines about [Contributing to HyperFormula](https://hyperformula.handsontable.com/docs/guide/contributing.html) and I confirm that my code follows the code style of this project. - [x] I have signed the [Contributor License Agreement](https://goo.gl/forms/yuutGuN0RjsikVpM2). - [x] My change is compliant with the [OpenDocument](https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocument-v1.3-os-part4-formula.html) standard. — not applicable; no engine behaviour changes. - [x] My change is compatible with Microsoft Excel. — not applicable; no engine behaviour changes. - [x] My change is compatible with Google Sheets. — not applicable; no engine behaviour changes. - [ ] I described my changes in the [CHANGELOG.md](https://github.com/handsontable/hyperformula/blob/master/CHANGELOG.md) file. — **intentionally not done.** Nothing user-facing changed; the published package is identical. - [x] My changes require a documentation update. — `dev-docs/` at both levels, `docs/README.md` and the public building guide are updated in this change. - [ ] My changes require a migration guide. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
sequba
requested review from
Tobiadefami,
adrianspdev,
marcin-kordas-hoc and
qunabu
September 1, 2026 11:02
… file Each of these links resolved to a file that exists, so no link checker flagged them; they just did not point where their label said. - `hyperformula/AGENTS.md:5` targeted `../dev-docs/README.md`, the repository-level index that line 6 already links. The package-level index — the only page listing the architecture, parser, interpreter, dependency-graph, function-catalogue, i18n and performance references — was therefore reached from nothing that loads automatically. - Both halves of the BUILD split targeted a bare `BUILD.md`, which resolves to the page doing the linking. Neither level could reach the other, so a reader needing the intermediate build and packaging (engine page) together with install, deploy and release (repository page) had no path between them. - `dev-docs/BUILD.md:35` named `script/prepare-cf-assets.js`. Spelled from the repository root, as this directory's convention requires, that is the repository-level `script/`, which holds only `release/` and the licence gate. The file is `docs/script/prepare-cf-assets.js`, as line 25 of the same page already says. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
… packaging check - `hyperformula/dev-docs/TESTING.md:5` targeted a bare `TESTING.md`, which resolves to the page doing the linking, so the repository-level testing standards — what a change must prove, how a case is written, and the ways a run must never be made green — were unreachable from the page an agent working in `hyperformula/test/` is routed to. Same defect as the two BUILD pages. - The packaging section read as though `verify:publish-package` sat outside the default path. `verify-bundles` is `run-p verify:**`, whose glob matches it, and `bundle-all` ends in `verify-bundles`, so every `bundle-all` already runs `npm pack` through the check. Say so, and keep the standalone case for a change that does not rebuild. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
…s them `hyperformula/dev-docs/TESTING.md:22` promised how the fetch works and the environment variables it honours, and targeted the repository-level `dev-docs/README.md`, which documents neither. The mechanics are in `hyperformula/test/README.md`, the file the link was already labelled with. The `WORKTREES.md` link on the same line was correct and is unchanged. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
`docs/AGENTS.md:5` promised what the directory contains, what is generated, and how to run and extend it, and targeted the repository-level `dev-docs/README.md` — which line 8 already links for everything else. The portal's own `README.md` is what carries that, including the deployment section `dev-docs/BUILD.md` defers to. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
…tead of a README - `.claude/skills/hyperformula-unit-testing/SKILL.md:14` promised how the private suite is fetched and the environment variables it honours, and targeted the repository-level `dev-docs/README.md`. With the two engine pages already corrected, the file that documents the branch-matched suite is now reachable from every place that offers it. - `dev-docs/DOC-STANDARDS.md:66` sent an author to the portal's README for VuePress mechanics through a bare `README.md`, which from `dev-docs/` resolves to this directory's own index. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
- `DOC-STANDARDS.md:27` sent a reader with a missing `docs/api/` to `npm run docs:build`, which regenerates the function docs, builds and verifies every bundle and then builds the whole VuePress site. The folder is written by `typedoc:build-api` alone, which the table three lines above already names as its generator. - `STRUCTURE.md:114` recorded the opposite of what the branch ships: it said a single root `dev-docs/` had been chosen and an engine-scope copy dropped. The two-level split is what exists, is drawn in this file's own tree and explained under "`dev-docs/` at two levels", so the decision bullet now records that and links to it. Nothing else in the tree claimed a single directory. - `STRUCTURE.md:47` described `.claude/` as holding hooks. It holds `settings.json` and `skills/`; the PostToolUse lint hook was removed in 83eb116 and `AGENT-TOOLING.md` already documents only the plugin list and the deny rules. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
…the metadata tables - Both skills linked a `TESTING.md` anchor at the wrong level, in opposite directions: the function-dev skill wanted "what each kind of change needs", which only the engine page has, and the code-review skill wanted "a test must prove behaviour", which only the repository page has. Each now points at the level that carries its heading. Every anchored cross-reference in the skills, both dev-docs levels and the AGENTS.md files now resolves. - Three pages called the public API surface `HyperFormula.ts` "and the types it exports". That file exports one class; the types are re-exported from `src/index.ts`, which is what the typings build emits. Someone auditing a type change for a break was being pointed at the wrong file. - The metadata tables omitted `enableArrayArithmeticForArguments` and `emptyAsDefault`, and said nothing about `arrayFunction` and `arraySizeMethod` being deprecated since 3.1.0 in favour of them and of `sizeOfResultArrayMethod`. Every field the interface declares is now either in a table or named as superseded, which is what the function-dev skill promises of this page. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
- `PERFORMANCE.md` opened with the same sentence twice, an artefact of the CODE-STYLE split; the copy that carries the cross-link stays. - The convention paragraph in `dev-docs/README.md` illustrated both of its rules with paths this move deletes — `../test/README.md` as a link target and `src/interpreter/plugin/` as a prose path. It is the paragraph an author is sent to before writing a link, so the examples were being copied forward. - `ARCHITECTURE.md` named `absolutizeDependencies.ts`, `parser/` and `LazilyTransformingAstService.ts` without the `hyperformula/` prefix every other path in the file uses. The directory attributions were already right; only the prefix was stale. The same leftover remains at `hyperformula/dev-docs/PARSER.md:14`, which was outside the reported finding. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB3b2sbMKo2Zxf8GHojkjc
The dashboard build command is `npm run docs:build:cf`, a root script: it needs `bundle-all` and `typedoc:build-api`, which only exist at the root. Workers Builds uses one root directory for the build and the deploy, so the deploy commands run from the root too — and `wrangler.jsonc` moved into `docs/` in this branch. `npx wrangler deploy` at the root therefore finds no configuration, and no local `wrangler` either, since it is a `docs/` devDependency. The documented commands are now the root scripts that already wrap it: `docs:deploy:cf` and `docs:preview:cf`. Three places said otherwise — the trigger table, the dashboard settings paragraph, and the header comment in `wrangler.jsonc` — and the paragraph between the first two claimed the commands run "from `docs/`", which the dashboard has no way to arrange alongside a root build command. The settings paragraph now also records the root directory, the setting that ties the two halves together. The hand-run block had the same defect in `npx wrangler dev`. Added `dev:cf` in `docs/`, and `docs:dev:cf` at the root, mirroring the existing `deploy:cf`/`preview:cf` pair, so every command in that block is a root script that works from a fresh checkout. The Cloudflare dashboard still has to be changed to match; this commit only makes the repository describe the configuration that can work. Documentation and tooling only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
`.gitignore` and `STRUCTURE.md` both described a `prepack` that copies the root `CHANGELOG.md` into the package and a `postpack` that removes it again. Neither script existed. The only copy was the explicit `copy-changelog`, called by `verify:publish-package` and by the release script just before it publishes — so the real release path was safe, but `npm publish --workspace=hyperformula` or a bare `npm pack` shipped a tarball with no changelog, which is the one file `check-publish-package.js` lists in FILES_CHECKLIST and `.npmignore` un-ignores by name. `prepack` and `postpack` now exist and do what both texts already claimed, so the guarantee holds for every path that packs or publishes the package rather than only for the release script. `npm pack` gained `--silent` in `verify:publish-package`. npm writes its lifecycle banners to stdout, not stderr, so without it the new hooks would print four `> hyperformula@3.4.0 prepack` lines into the pipe that feeds `check-publish-package.js`, which takes its whole first stdin chunk as the tarball name and would fail to open it. The explicit `copy-changelog` in `verify:publish-package` is kept, redundant though it now is: a prepack failure inside `npm pack | node ...` is swallowed by the pipeline exit status, so that leading `&&` is what still makes the check fail loudly when the changelog cannot be copied. Verified by packing the real package: the tarball carries `package/CHANGELOG.md` with the root file's contents, and no copy is left behind afterwards. Tooling only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
…ependencies `hyperformula/package.json` is a copy of the manifest that used to sit at the repository root, so its `repository` object still described a package living there. npm resolves "repository" links against that object, so the package page pointed at the repository root rather than at the directory the sources now occupy. Added `"directory": "hyperformula"`, which is what npm reads for a package published from a monorepo subdirectory. Two devDependencies came along with that copy and have nothing to do with the engine: - `@vuepress/shared-utils` is imported by `docs/.vuepress/highlight.js`, the search-box plugin, and `docs/script/renderBuiltinFunctionsTable.ts` — all under `docs/`, which installs separately and already declares it. The split is the reason `docs/script/` exists at all, as `dev-docs/BUILD.md` records. - `full-icu` has no reference anywhere: no import, no `NODE_ICU_DATA`, no `--icu-data-dir`. `package-lock.json` regenerated with `npm install --package-lock-only`. The diff is entirely the two pruned trees; the only additions are `peer`, `optional` and `license` annotations npm re-derives for packages that were previously reached through them. No version moved, and the lockfile still satisfies every remaining devDependency. Tooling only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
…ine README The directory table had a blank line and then a paragraph between the `hyperformula-ui/` and `docs/` rows. A blank line ends a GitHub-Flavored Markdown table, so the `docs/` row was not part of it: with no blank line after the paragraph either, it rendered as a lazy continuation of that sentence, pipes and all. The row that did not render is the one explaining that `docs/` installs separately and is not a workspace member — the fact a newcomer running `npm ci` most needs. The paragraph now follows the table. The engine row said "its [README] is the product documentation" and linked `dev-docs/README.md`, the index of the internal developer documentation. It points at `hyperformula/README.md`. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
Three groups, one cause: each names a path that this branch relocated. Repository-internal prose: - `docs/README.md:72` and `FunctionDescription.ts:8` and `:20` name `script/prepare-cf-assets.js` and `script/renderBuiltinFunctionsTable.ts`. Both generators moved to `docs/script/`; the root `script/` now holds only `release/` and `check-licenses.mjs`. - `dev-docs/AGENT-TOOLING.md:34` tells an agent to read `src/` rather than the generated documentation when it wants to know what the code does. There is no `src/` at the root. Published guide pages, which are worse because the reader cannot see the repository to correct for it: `key-concepts.md:100`, `localizing-functions.md` and `building.md` deep-link `blob/master/src/parser/FormulaParser.ts`, `tree/master/src/i18n/languages` and `blob/master/test/README.md`. They still resolve today because `master` predates the move, and become 404s on the documentation portal the moment it lands. `docs/guide/contributing.md:50` told contributors to add specs to the `test` folder. `CONTRIBUTING.md` symlinks to this file and the new `README.md` sends external contributors to it, so it is the first instruction a first-time contributor follows, and the move never touched it. Documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
…ted to be `hyperformula/dev-docs/TESTING.md:28` calls `npm run test` "Lint, Jest, and the Karma browser run — the full local gate". Only the root script linted; the engine's was `npm-run-all test:jest test:browser`. `hyperformula/AGENTS.md` tells a contributor to run package scripts from the package directory, so the reading that skipped lint was the likely one, and the branch it produced fails `lint.yml` after being believed green. The engine `test` now runs lint first. Linting stays root-owned, as `STRUCTURE.md` records — one pass over the whole repository — so the new `lint` script delegates upward with `npm --prefix .. run lint`, which runs the root script with the root as its working directory. That matters: `.eslintignore`, `.eslintrc.js` and its `tsconfigRootDir` all anchor there, so a lint started inside `hyperformula/` would resolve none of them. No CI job calls the engine `test` — `test.yml` runs `test:ci` and `test:browser`, and `lint.yml` runs lint once — so this adds no CI work. Also split the unit-testing skill's `TESTING.md` row, which promised "how to write a case" from the engine page. That section is "How to write a test case" on the repository-wide page; the engine page covers the suites, running them, and per-change coverage. The row named one file for both. Each half now points at the level that holds it, the second anchored at the section. Tooling and documentation only; no changelog entry required. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ngxDaE2MeedcUvki2g4wN
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1757 +/- ##
========================================
Coverage 97.32% 97.32%
========================================
Files 195 195
Lines 15739 15739
Branches 3390 3390
========================================
Hits 15318 15318
Misses 421 421
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
HF-359. Turns the repository into a monorepo, and replaces the single always-loaded
DEV_DOCS.mdwith a documentation system that survives having more than one package in the tree.No changes under
hyperformula/src/. Every file there is byte-identical to its old location atsrc/; git records the moves as renames throughout, so history follows. The published package's name, version, entry points andexportsmap are unchanged, and the publish-package check confirms the tarball contents.The monorepo
docs/left the workspace. VuePress 1.x and its--openssl-legacy-providertree must never reach an engine install.npm run docs:installinstalls it; CI installs it before building the portal.wrangler.jsoncandworker/moved underdocs/, and the asset directory is now./.vuepress/dist.CHANGELOG.mdstays at the root.src/andtest/moved together, the depth from a spec to the package root is unchanged.Everything lives with what owns it
dev-docs/hyperformula/dev-docs/— architecture, parser, interpreter, dependency graph, function catalogue, i18n, performance, its suites, its buildscript/release/,check-licenses.mjshyperformula/script/— build checks;docs/script/— the generator and the Worker assetsThree
dev-docspages were genuinely mixed and were split, not moved:TESTING(what a change must prove stays; the suites go down),BUILD(workspace, deploy and release stay; the intermediate build and packaging go down), andCODE-STYLE(style stays; the hot-path table becomeshyperformula/dev-docs/PERFORMANCE.md).The
script/split was decided by the callers rather than by judgement: every script had exactly one caller and the caller named its owner. Twelve call sites stopped reaching across a package boundary.Root
examples/is deleted — three PNGs were byte-identical duplicates ofdocs/.vuepress/public/, which is what the site actually serves, and the other five files were referenced nowhere.The documentation
DEV_DOCS.mdsaid "start by reading entire DEV_DOCS.md". Every agent working anywhere loaded the docs-portal conventions, the i18n sources and the function-catalogue rules before it could look at a parser bug. Three layers replace it:AGENTS.mddev-docs/.claude/skills/dev-docs/is the single source of truth, and nothing outside it restates what is in it: everyAGENTS.md,README.mdandSKILL.mdcarries only what is so specific to its own directory that it would be useless elsewhere, and links for the rest.AGENTS.mdTwelve
AGENTS.mdfiles, each with aCLAUDE.mdsymlink so Claude Code and Cursor read one source. Nine skills, five scoped by apathsglob..claude/settings.jsonenablestypescript-lsprepository-wide and denies agent reads of build artifacts — but not of generated documentation, which is meant to be read..worktreeincluderecords what must never be copied into a worktree and why..ai/holds one sentence pointing atdev-docs/, for agents that look there by habit.DOCS_CONTENT_GUIDE.mdmoved in asdev-docs/DOCS-CONTENT-GUIDE.md.How did you test your changes?
npm cinpm run lintnpm run test:jestnpm run test:ci -- --coveragehyperformula/coveragenpm run test:browsernpm run verify:typingstsc --noEmitcleannpm run bundle-allPublish package check: OKnpm run check:licensesnpm run release code-freeze …(dry run)npm run docs:generate-function-docsnpm --prefix docs run build:cf_headersand_redirectscopiedEvery relative Markdown link in
AGENTS.md, bothdev-docs/levels, the skills and the READMEs was resolved against the filesystem. All resolve.What the reviews found
Four rounds, 58 findings. The ones worth knowing about:
Flag arguments were being swallowed. Every delegating root script dropped them, so
npm run test:ci -- --coveragereached the workspace as barejest --forceExitand CI had been uploading nothing to Codecov while staying green. Demonstrated withnpm run test:jest -- --listTests, which ran all 6232 tests instead of listing them.The release script was never adapted. It read and wrote
./package.json— now the private root manifest with noversion— and published from the root, where npm refuses a private package. Two dry runs surfaced two further paths no review had caught.check:licenseswas checking nothing.license-checkerreads<start>/node_modules, and npm hoists, so fromhyperformula/it saw an empty tree and from the root a manifest with no dependencies — zero packages either way, passing vacuously. Replaced withscript/check-licenses.mjs, driven bynpm ls --omit=dev.The portal was publishing internal agent instructions. VuePress sweeps every markdown file under
docs/, sodocs/AGENTS.mdand its symlink were being built into/AGENTS.htmland an identical/CLAUDE.html, sitemap and search index included.npm run test:browserexited non-zero after passing. The cause wasclient.clearContext: falsein the Karma base config, and it reproduced on the pre-move tree.concurrency: 1was the obvious first guess and is wrong — it doubles wall-clock time and makes the failure deterministic rather than removing it.An audit traced the deleted
DEV_DOCS.mdinto the new tree. Almost nothing was lost; what it found was new prose that had invented things.dev-docs/README.mdstated a link convention that every one of the 33 links in the directory violated, andI18N.mdclaimed argument separators are part of the language package when they areConfigParams.One finding was rejected on measurement — a claimed ESLint timeout risk, measured at 0.91s and 1.06s.
Two pre-existing problems this uncovered
The source language packs have never been linted. The old ignore list carried a bare
languagesentry meant for the build output. An unanchored pattern matches a directory of that name at any depth, so it also excludedsrc/i18n/languages/— and thesort-keysoverride targeting exactly those files has never run. Anchoring the build-output entry exposed 881 violations across 19 translation files. Confirmed against the pre-move tree, where ESLint reportsFile ignored because of a matching ignore pattern.They are excluded again in
.eslintignore, deliberately and with a comment, so this branch keeps the exact pre-move lint baseline. Sorting 19 translation files is a change of its own — and reordering translation entries is what the i18n guidance tells contributors not to do casually.npm run test:compatibilitywas broken for everyone, pointing at a path in neither repository. Fixed here, since the branch had to touch it anyway.Not done, on purpose
CI path filtering. A naive
paths:filter on a workflow that branch protection lists as a required check leaves it permanently pending and makes pull requests unmergeable. Doing it safely needs the required-checks list, which lives in repository settings, plus thedorny/paths-filter-and-single-gate shape. The CI paths are all updated; only the filtering is outstanding.Importing
hyperformula-ui— left for the teammate who owns the formula-builder move. It is deliberately not in theworkspacesarray until then: npm silently ignores an entry with nopackage.json.Both are recorded in
dev-docs/STRUCTURE.mdunder "What the move still owes", so they survive the pull request.Types of changes
Repository structure and tooling. The published package is unchanged.
Related issues:
Checklist:
dev-docs/at both levels,docs/README.mdand the public building guide are updated here.Companion pull request
handsontable/hyperformula-tests#51 gives the private test suite an
AGENTS.mdpointing here, and corrects three claims in itsREADME.md.🤖 Generated with Claude Code