Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .changeset/published-readme-docs-links-absolute.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
---
"@objectstack/service-automation": patch
"@objectstack/service-analytics": patch
"@objectstack/service-knowledge": patch
"@objectstack/knowledge-ragflow": patch
"@objectstack/service-cache": patch
"@objectstack/service-i18n": patch
"@objectstack/service-job": patch
---

Published READMEs link to the docs site in the one form that works on npm, on GitHub and on the docs site (#9632)

**Seven docs links in these READMEs pointed nowhere.** They were spelled as a repo
path rooted at `/` — `[Flows](/content/docs/automation/flows.mdx)` — and a README in a
package's `files` array with `private` unset is rendered on the **npm package page** and
on **GitHub**, not only in this repository. There a root-relative href resolves against
`npmjs.com` and `github.com` respectively. It was not a docs-site route either:
`apps/docs/lib/source.ts` mounts `loader({ baseUrl: '/docs' })` over `content/docs`, so
the route for that first link is `/docs/automation/flows`, and `apps/docs/redirects.mjs`
carries no `/content` source that would rescue the written form. Every target page
existed and every one of them was reachable — only the links were not.

All seven now use the absolute form the repo had already established in
`create-objectstack`'s published READMEs: `https://docs.objectstack.ai/docs/...`, with
the path taken under `content/docs` and the page extension dropped, because the route
carries none. Each target was re-verified at the route level rather than as a file — the
two that named a **directory** (`/content/docs/automation/`,
`/content/docs/references/automation/`) resolve only because those directories carry an
`index.mdx`; a directory without one is a 404, not a section.

**Two more links in the same class were converted in the same pass.**
`service-knowledge` and `knowledge-ragflow` pointed at
`../../../content/docs/protocol/knowledge.mdx`. Those relative paths do resolve on both
GitHub and npm, so they are a milder defect than the seven — but they land the reader on
**raw MDX source** instead of the rendered page. They now point at the rendered page as
well. `service-knowledge`'s link text changed with it: it was the source filename in a
code span, which stops being an honest label once the destination is the page.

No API, behaviour or type surface changes — this is the published documentation these
packages ship.
32 changes: 32 additions & 0 deletions .github/workflows/lint.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,6 +390,38 @@ jobs:
- name: Docs redirect destinations resolve, and no chains
run: pnpm check:docs-redirects

# #9632 published-README links: a README in a package's `files` array with
# `private` unset is rendered on npm and on GitHub as well as here, and
# NOTHING read its links. Measured before the gate was written: the lychee
# lane globs `content/**` plus the ROOT README.md/ARCHITECTURE.md, never
# `packages/**/README.md`; `check:doc-anchors` takes the same two
# EXTRA_SOURCES; `check:adr-links` is scoped to docs/adr/;
# `check:published-readme-exports` has exactly the right population but
# reads FENCED CODE BLOCKS ONLY and has no notion of a link. So seven links
# across five published service packages sat spelled `/content/docs/...` —
# a form that resolves on none of the three surfaces — and shipped to npm
# green. An npm tarball outlives any in-repo correction, which is why the
# gate is worth more than the seven fixes that came with it.
#
# Strict, with no baseline: the census that sized it found 149 outbound
# links across all 60 published markdown files, so per-link assertions are
# affordable. It reuses rather than re-derives — the POPULATION comes from
# check-published-readme-exports (`publishedDocs`), the page resolver from
# check-docs-redirects (`pageCandidates`, including its
# directory-without-an-index-is-a-404 subtlety), the heading ids from
# check-doc-anchors (`headingIds`), and the fence/code-span stripping from
# check-adr-links. Two gates deriving "published" separately would disagree
# the first time a `files` array changed, silently, each still green.
#
# This job for the same reason check:docs-redirects is here: it is a
# dependency-free filesystem check and this job carries "the whole check:*
# gate family". Runs its own --self-test first, where all three assertions
# are observed FAILING as well as silent — over the real tree the strict
# assertion has a small population, so a green run cannot by itself
# distinguish a working scanner from one that matches nothing.
- name: Published-README links are followable off the docs site
run: pnpm check:published-readme-links

# #9018 docs image tags: the same "example image tag N majors behind
# packages/cli" staleness was found and hand-fixed TWICE, independently —
# content/docs/deployment/self-hosting.mdx (#8911, PR #8960) and
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -99,6 +99,7 @@
"check:type-source-resolution": "node scripts/check-type-source-resolution.mjs --self-test && node scripts/check-type-source-resolution.mjs",
"check:published-files": "node scripts/check-published-files.mjs --self-test && node scripts/check-published-files.mjs",
"check:published-readme-exports": "node scripts/check-published-readme-exports.mjs --self-test && node scripts/check-published-readme-exports.mjs",
"check:published-readme-links": "node scripts/check-published-readme-links.mjs --self-test && node scripts/check-published-readme-links.mjs",
"check:type-check-coverage": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs",
"check:type-check-debt": "node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs --re-measure",
"check:driver-conformance": "node scripts/check-driver-conformance.mjs --self-test && node scripts/check-driver-conformance.mjs",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/knowledge-ragflow/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,7 @@

[RAGFlow](https://github.com/infiniflow/ragflow) `IKnowledgeAdapter` for ObjectStack.

Bridges the [Knowledge Protocol](../../../content/docs/protocol/knowledge.mdx) to a RAGFlow deployment via its HTTP API. RAGFlow handles chunking (DeepDoc), embedding, hybrid retrieval, and reranking; ObjectStack handles metadata-native sources and permission-aware filtering on top of the returned hits.
Bridges the [Knowledge Protocol](https://docs.objectstack.ai/docs/protocol/knowledge) to a RAGFlow deployment via its HTTP API. RAGFlow handles chunking (DeepDoc), embedding, hybrid retrieval, and reranking; ObjectStack handles metadata-native sources and permission-aware filtering on top of the returned hits.

## Why RAGFlow?

Expand Down
2 changes: 1 addition & 1 deletion packages/services/service-analytics/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -191,4 +191,4 @@ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).

- [@objectstack/objectql](../../objectql/)
- [@objectstack/driver-memory](../../drivers/driver-memory/) — ships `InMemoryStrategy`
- [Analytics Guide](/content/docs/data-modeling/analytics.mdx)
- [Analytics Guide](https://docs.objectstack.ai/docs/data-modeling/analytics)
6 changes: 3 additions & 3 deletions packages/services/service-automation/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -175,7 +175,7 @@ array of `{ field, operator, value }` triples; operator objects such as
through `filter`. Unknown keys are rejected at `registerFlow()`.

For every other node's `config`, and for loops, parallel blocks, subflows, waits
and error handling, see the maintained reference — **[Flows](/content/docs/automation/flows.mdx)**.
and error handling, see the maintained reference — **[Flows](https://docs.objectstack.ai/docs/automation/flows)**.
This README deliberately does not keep a second copy of that per-node reference.

## Expressions
Expand DownExpand Up@@ -456,5 +456,5 @@ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).
## See Also

- [@objectstack/spec/automation](../../spec/src/automation/)
- [Flow Builder Guide](/content/docs/automation/)
- [Trigger Reference](/content/docs/references/automation/)
- [Flow Builder Guide](https://docs.objectstack.ai/docs/automation)
- [Trigger Reference](https://docs.objectstack.ai/docs/references/automation)
2 changes: 1 addition & 1 deletion packages/services/service-cache/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -148,4 +148,4 @@ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).
## See Also

- [@objectstack/spec/contracts](../../spec/src/contracts/)
- [Cache Service](/content/docs/kernel/contracts/cache-service.mdx)
- [Cache Service](https://docs.objectstack.ai/docs/kernel/contracts/cache-service)
2 changes: 1 addition & 1 deletion packages/services/service-i18n/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,4 +193,4 @@ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).
## See Also

- [@objectstack/spec/system](../../spec/src/system/) — the `translation` metadata schema
- [I18n Standard](/content/docs/protocol/kernel/i18n-standard.mdx)
- [I18n Standard](https://docs.objectstack.ai/docs/protocol/kernel/i18n-standard)
2 changes: 1 addition & 1 deletion packages/services/service-job/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -183,4 +183,4 @@ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).

- [@objectstack/spec/contracts](../../spec/src/contracts/)
- [Cron Expression Generator](https://crontab.guru/)
- [Queue Service](/content/docs/kernel/runtime-services/queue-service.mdx)
- [Queue Service](https://docs.objectstack.ai/docs/kernel/runtime-services/queue-service)
2 changes: 1 addition & 1 deletion packages/services/service-knowledge/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ Orchestrator implementing `IKnowledgeService` over pluggable
— that's the job of adapter plugins (`knowledge-memory`,
`knowledge-ragflow`, …).

See [`content/docs/protocol/knowledge.mdx`](../../../content/docs/protocol/knowledge.mdx).
See [Knowledge Protocol](https://docs.objectstack.ai/docs/protocol/knowledge).

## License

Expand Down
16 changes: 11 additions & 5 deletions scripts/check-docs-redirects.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,7 +102,7 @@

import { existsSync, mkdirSync, mkdtempSync, rmSync, statSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

/** The docs site's `baseUrl` (apps/docs/lib/source.ts). */
Expand DownExpand Up@@ -685,8 +685,14 @@ async function main() {
process.exit(report(findings, stats, 'apps/docs/redirects.mjs'));
}

if (process.argv.includes('--self-test')) {
await selfTest();
} else {
await main();
/* Run only when invoked as a program — `docsRelative`, `pageCandidates` and
* `firstMatchingSource` are exported so a sibling gate can ask "would Fumadocs
* serve this /docs/... URL?" without the import itself checking the redirect
* table (and calling `process.exit` out from under its caller). */
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
if (process.argv.includes('--self-test')) {
await selfTest();
} else {
await main();
}
}
49 changes: 37 additions & 12 deletions scripts/check-published-readme-exports.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -126,6 +126,7 @@
import { existsSync, readFileSync, readdirSync } from 'node:fs';
import { join, posix, resolve } from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import ts from 'typescript';

// Anchored to the script, not to cwd: the verdict must not depend on where the
Expand DownExpand Up@@ -682,7 +683,20 @@ function freshRemedy() {
// Run
// ---------------------------------------------------------------------------

function run() {
/**
* THE published-markdown population, derived once for every gate that needs it.
*
* "Published" is `private` unset AND a non-empty `files` array AND the file
* matched by one of its patterns, minus `MARKDOWN_EXCLUDED`. That definition is
* load-bearing for more than one gate now, and two gates deriving it separately
* would disagree the first time a package's `files` array changed — silently,
* each still green. So it is computed HERE and imported, never re-derived.
*
* @param {string} [caller] gate name to attribute an empty-population error to
* @returns {{ members: {dir: string, manifest: any}[], byName: Map<string, any>,
* docs: {pkg: string, file: string, text: string}[] }}
*/
export function publishedDocs(caller = SELF) {
const members = workspaceDirs().map((dir) => ({
dir,
manifest: JSON.parse(readFileSync(join(ROOT, dir, 'package.json'), 'utf8')),
Expand All@@ -703,9 +717,16 @@ function run() {
});
}
}
// A scan that read nothing is the #4690 failure: indistinguishable from a
// clean tree in the output, and green either way. Never a skip.
if (docs.length === 0) {
throw new Error(`${SELF}: no published markdown found — the scan read nothing (#4690).`);
throw new Error(`${caller}: no published markdown found — the scan read nothing (#4690).`);
}
return { members, byName, docs };
}

function run() {
const { byName, docs } = publishedDocs();

// Pass 1: which workspace type entries do the READMEs actually reach?
const targets = new Map(); // "<name><subpath>" -> { name, subpath, abs, declared, missing }
Expand DownExpand Up@@ -1187,14 +1208,18 @@ function selfTest() {
);
}

if (process.argv.includes('--self-test')) {
selfTest();
process.exit(0);
}

try {
process.exit(run());
} catch (err) {
console.error(`✗ check:published-readme-exports — ${err.message}`);
process.exit(1);
/* Run only when invoked as a program — `publishedDocs` and the extractors are
* exported so a sibling gate can reuse this gate's population without the
* import itself building a TypeScript program and sweeping the workspace. */
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
if (process.argv.includes('--self-test')) {
selfTest();
process.exit(0);
}
try {
process.exit(run());
} catch (err) {
console.error(`✗ check:published-readme-exports — ${err.message}`);
process.exit(1);
}
}
Loading
Loading