Skip to content
Open
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
37 changes: 37 additions & 0 deletions .github/workflows/pages-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,28 @@ jobs:
permissions:
contents: read
deployments: write
statuses: write
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: preview
url: ${{ steps.pages.outputs.pages-deployment-alias-url }}
steps:
- name: Mark the pull-request preview pending
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PREVIEW_HEAD_SHA: ${{ needs.identity.outputs.head_sha }}
with:
script: |
await github.rest.repos.createCommitStatus({
...context.repo,
sha: process.env.PREVIEW_HEAD_SHA,
context: 'docs preview',
state: 'pending',
description: '正在构建并部署预览',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
})

- name: Checkout the trusted preview controller
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -142,3 +158,24 @@ jobs:
CLOUDFLARE_PAGES_DEPLOYMENT_ID: ${{ steps.pages.outputs.pages-deployment-id }}
CLOUDFLARE_PAGES_DEPLOYMENT_URL: ${{ steps.pages.outputs.pages-deployment-alias-url }}
INKCRE_PAGES_SMOKE_MODE: preview

- name: Report the preview result on the pull-request commit
if: ${{ always() }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PREVIEW_HEAD_SHA: ${{ needs.identity.outputs.head_sha }}
PREVIEW_RESULT: ${{ job.status }}
PREVIEW_URL: ${{ steps.pages.outputs.pages-deployment-alias-url }}
with:
script: |
const succeeded = process.env.PREVIEW_RESULT === 'success'
await github.rest.repos.createCommitStatus({
...context.repo,
sha: process.env.PREVIEW_HEAD_SHA,
context: 'docs preview',
state: succeeded ? 'success' : 'error',
description: succeeded ? '预览已就绪,点击查看' : '预览失败或取消,点击查看日志',
target_url: succeeded
? process.env.PREVIEW_URL
: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
})
8 changes: 8 additions & 0 deletions 20-product-tdd/cross-unit-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Record durable data and behavior contracts that span more than one unit reposito
- `running` means the current runtime has actually started that extension and applied its runtime side effects.
- Starting or stopping an extension is not a pure flag flip; it changes runtime capabilities and API surface for that client.

## Extension Documentation Hosting Contract

- Author-built static documentation, exact Release/scope identity, conditional whole-site
corrections, snapshot-origin isolation, and consumer discovery follow the
[Extension Documentation Hosting Contract](extension-documentation-hosting.md).
- Registry owns hosting and executable API details; authors own site content and structure.
Documentation does not become an installable Distribution or alter Host lifecycle authority.

## Info-Base Ownership Contract

- Collection, organization, application, graph authority, resolver/storage composition, and
Expand Down
103 changes: 103 additions & 0 deletions 20-product-tdd/extension-documentation-hosting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Extension Documentation Hosting

## Purpose And Ownership

This contract defines how Extension authors, Registry, developer tooling, and consuming Hosts
exchange hosted documentation. Registry owns the executable HTTP contract and hosting mechanics;
this document owns the boundaries that those implementations must preserve.

Authors build a static site with their chosen tools. Registry accepts and serves the resulting
files without running builds, interpreting document structure, or rewriting HTML and JavaScript.
Navigation, search, localization, themes, and interface-specific instructions belong to the
author's site. Documentation is not an executable Extension Distribution and cannot make a Release
installable by itself.

## Identity And Document Ownership

A documentation set belongs to one Extension name, one exact Release version, and one scope:

| Scope | Meaning | First-party source owner |
| --- | --- | --- |
| `global` | Extension-wide concepts, workflows, and limits across channels | `core-py` |
| `python` | Python Distribution operation and configuration | The Python producer in `core-py` |
| `module-federation` | Web Distribution interfaces and behavior | The MF producer in `client-web` |

These are separate, optional sites, not inheritance or override layers. Registry does not merge
them. A channel-specific set requires the corresponding Distribution association on that Release;
global documentation does not require a Web Distribution. Web, CLI, and Agent instructions are
reader interfaces, not additional Distribution channels.

First-party sites share a template maintained in `InKCre/docs`, with producer repositories using
an explicit template revision. This does not require ecosystem authors to adopt that template or
its static-site generator. The main documentation site owns application onboarding, self-hosting,
and cross-Extension guidance; detailed Extension instructions retain one source owner rather than
being copied into each consuming site.

## Discovery And Consumption

Registry exposes documentation discovery separately from Release installation metadata. Consumers
ask their configured Registry for the exact installed Release and receive the available scopes,
stable entry addresses, current content identities, update times, and publication provenance.
Adding documentation must not invalidate existing strict Release consumers.

An absent scope is genuinely absent. Consumers may explicitly offer global documentation instead
of a missing channel site, but must not silently substitute another scope or version. Page paths
inside a site remain an agreement between its author and consumers, not Registry-defined content
semantics. Consumers follow Registry-provided entry addresses rather than constructing content
hostnames or assuming documentation shares the management origin.

## Publication And Corrections

Publishing a set uploads a complete static bundle and identifies its entry file. Registry validates
the bundle's paths, entry, file types, and resource bounds; it does not accept server-side execution
or author-defined server configuration. Detailed archive, routing, MIME, and error contracts belong
to Registry's executable API and local documentation.

Namespace publication authority also controls that namespace's documentation. Creating a set
requires a conditional create; replacing it requires the observed current entity tag. Only after
all files are available may Registry atomically move that set's entry to the new snapshot. A failed
upload or conflicting replacement leaves the previous entry intact. Clients resolve uncertain
responses by reading the resulting content identity before retrying the same candidate.

Documentation can be corrected without a new Extension Release. There is no separate documentation
semantic version, automatic inheritance between Releases, or many-to-many applicability mapping.
Updating one scope does not update another or mutate Python/MF artifacts. Snapshot content is
immutable; its identity serves integrity and resource consistency, not a second author-managed
version scheme.

Previously published snapshots remain available while their owning Release permits public reads,
so an already opened page can finish loading its original resources. Each public snapshot remains
bound to its exact Release and scope even when underlying byte storage is deduplicated.

## Static Content Boundary

Each snapshot is served at the root of its own content origin, separate from Registry management
and from other snapshots. A stable Release/scope entry selects the current snapshot; navigation and
assets within that site continue to use that snapshot. This prevents mixed-version assets and
separates script, browser storage, and Service Worker authority. Separate URL directories on one
origin do not satisfy this boundary.

Content origins do not expose publication APIs or receive Registry publication credentials or
shared authentication cookies. Registry controls response headers and MIME handling; uploaded
files cannot supply server headers or override host routing. Registry UI must not execute author
content within its management origin, and links to hosted sites sever opener access.

The serving topology must support root-relative static assets without rewriting the author's build.
When an author needs an absolute hostname during a build, tooling may reserve an opaque snapshot
address before computing the final content digest. An address can never be rebound to different
bytes. Concrete domain, DNS, TLS, and storage deployment choices remain Registry-owned.

## Release Lifecycle

Documentation may be prepared alongside a preparing Release, but preparing content is not public.
Published Releases expose their documentation normally. Yanked Releases retain exact-version
documentation, and Registry entry surfaces identify that withdrawal; documentation corrections do
not restore the Release's recommendation or installability.

Blocked Releases expose neither discovery, stable entries, nor current or historical snapshot
files. Publishers cannot bypass a block by correcting documentation. Lifecycle checks apply before
cache revalidation, and private object storage must not offer an unguarded public bypass.

These rules govern new network responses, not copies already downloaded by a reader or stored by
an author's Service Worker. Hosting does not promise remote deletion of offline content. First-party
templates do not enable offline Service Workers.
51 changes: 51 additions & 0 deletions website/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,63 @@ export default defineConfig({
},
themeConfig: {
nav: [
{ text: 'Getting Started', link: '/getting-started' },
{ text: 'Developer', link: '/developer/' },
{ text: 'About', link: '/about/' },
{ text: 'GitHub', link: 'https://github.com/InKCre' },
],
sidebar: {
'/': [
{ text: 'Getting Started', link: '/getting-started' },
{
text: 'User Guide',
items: [
{ text: 'Connect to Your Instance', link: '/guide/connect' },
{ text: 'CLI / Agent Connection', link: '/guide/connect-cli' },
{ text: 'Prepare an Extension', link: '/guide/extensions' },
{ text: 'Collect Your First Source', link: '/guide/first-source' },
{ text: 'Find What You Saved', link: '/guide/search' },
{ text: 'Schedule Collection and Indexing', link: '/guide/schedules' },
{
text: 'Connect More Sources',
link: '/guide/sources',
items: [
{ text: 'RSS and Atom', link: '/guide/sources/rss' },
{ text: 'GitHub Stars and Lists', link: '/guide/sources/github' },
{ text: 'Email over IMAP', link: '/guide/sources/mail' },
{ text: 'Telegram Inbox', link: '/guide/sources/telegram' },
{ text: 'Twitter / X Bookmarks', link: '/guide/sources/twitter' },
{ text: 'Memos-Compatible Capture', link: '/guide/sources/memos' },
{ text: 'Run a Collection', link: '/guide/collect' },
],
},
{
text: 'Use Your Information',
link: '/guide/daily-use',
items: [{ text: 'Sinks: ChatGPT via MCP', link: '/guide/sinks/chatgpt' }],
},
{ text: 'Troubleshooting', link: '/guide/troubleshooting' },
],
},
{
text: 'Self-Hosted',
items: [
{ text: 'Overview', link: '/self-hosted/' },
{ text: 'Getting Started', link: '/self-hosted/getting-started' },
{ text: 'Render and Neon', link: '/self-hosted/render-neon' },
{ text: 'Heroku and Neon', link: '/self-hosted/heroku-neon' },
{ text: 'Advanced', link: '/self-hosted/advanced' },
],
},
],
'/developer/': [
{
text: 'Ecosystem Developers',
items: [
{ text: 'Build on InKCre', link: '/developer/ecosystem/' },
{ text: 'Build a Source Extension', link: '/developer/ecosystem/source-extension' },
],
},
{
text: 'Developer Guide',
items: [
Expand Down
54 changes: 54 additions & 0 deletions website/.vitepress/theme/InterfaceGuide.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { inject, onMounted, ref, type Ref } from 'vue'

const preference = inject<Ref<string>>('guide-interface', ref('web'))
const ready = ref(false)
onMounted(() => {
ready.value = true
})
</script>

<template>
<div class="interface-guide">
<label v-if="ready" class="interface-guide__choice">
Instructions for
<select v-model="preference">
<option value="web">Web app (recommended)</option>
<option value="cli">CLI / Agent</option>
</select>
</label>
<p v-if="ready" class="interface-guide__hint">Your choice follows you between guide pages.</p>
<section v-show="!ready || preference === 'web'" aria-label="Web app instructions">
<slot name="web" />
</section>
<section v-show="!ready || preference === 'cli'" aria-label="CLI and Agent instructions">
<slot name="cli" />
</section>
</div>
</template>

<style scoped>
.interface-guide__choice {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
margin-top: 1.5rem;
}
select {
padding: 0.4rem 0.7rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
color: var(--vp-c-text-1);
background: var(--vp-c-bg-soft);
font: inherit;
}
select:focus-visible {
outline: 2px solid var(--vp-c-brand-1);
outline-offset: 2px;
}
.interface-guide__hint {
color: var(--vp-c-text-2);
font-size: 0.875rem;
}
</style>
11 changes: 10 additions & 1 deletion website/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import DefaultTheme from 'vitepress/theme'
import type { EnhanceAppContext } from 'vitepress'
import { ref } from 'vue'
import InterfaceGuide from './InterfaceGuide.vue'
import './custom.css'

export default DefaultTheme
export default {
extends: DefaultTheme,
enhanceApp({ app }: EnhanceAppContext) {
app.provide('guide-interface', ref('web'))
app.component('InterfaceGuide', InterfaceGuide)
},
}
32 changes: 26 additions & 6 deletions website/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ pnpm --dir website audit --audit-level high
- Future Chinese source will live under `content/zh/` and be published under `/zh/`.
- Only English is active until the Chinese route set is complete or the locale switch has a
deliberate fallback.
- The current published routes are `/`, `/developer/`, `/developer/architecture`,
`/developer/contributing`, and `/about/`.
- `/getting-started` owns the application-level What, Why, and How introduction.
- `/self-hosted/` contains its Getting Started path, Render/Heroku quick-deployment guides, and
Advanced guide. Its Getting Started page orders the journey rather than duplicating procedures.
- `/guide/` leaf pages own reusable client, collection, retrieval, scheduling, source, daily-use,
and troubleshooting procedures. Link to these pages from any onboarding path rather than to
sections buried inside the self-hosted walkthrough.
- `/guide/sources` selects independent source tutorials under `/guide/sources/`; `/guide/collect`
owns shared collection and Job observation. Memos is documented separately as write-in capture.
- `/developer/` separates ecosystem integration guidance under `/developer/ecosystem/` from
architecture and core contribution guidance; `/about/` describes the project.
- Section indexes use trailing-slash routes, such as `/developer/`.
- Leaf pages use lowercase ASCII kebab-case routes without an extension, such as
`/developer/architecture`.
Expand All @@ -44,6 +52,15 @@ are resolved from the rewritten route, not the source file location.

## Page Authoring

- User procedures default to client-web. Use the shared `InterfaceGuide` component with `#web` and
`#cli` slots for alternate steps on the same route; CLI instructions primarily serve Agents and
operators. The choice survives client-side navigation, not a full reload. Without JavaScript, both
sections remain readable. Keep shared prerequisites and limitations outside the slots.
- Set `outline: false` on interface-switching pages: the default VitePress outline includes hidden
slot headings. Do not expose links to invisible instructions. The site sidebar remains available.
- State actual interface gaps instead of implying feature parity. Core package installation and
lexical maintenance still need CLI/operator steps; a browser wizard requires a compatible native
distribution as well as its Core collector. Check both against published Registry releases.
- Keep exactly one H1 per page.
- Add a concise page `description` in frontmatter.
- Give headings explicit custom anchors only when another page or external consumer needs a durable
Expand All @@ -70,10 +87,13 @@ same-repository run, the trusted Preview workflow checks out that exact head, bu
publishes an isolated, deterministic, short-lived preview. Fork pull requests receive no preview
credentials, preview origins remain `noindex`, and closing the pull request replaces the live
preview with a trusted closed-preview tombstone. The stable `preview-docs-pr-N` branch alias is the
user-facing preview URL and is recorded against the pull-request head in GitHub; Cloudflare retains
the underlying immutable deployments in its history. If automatic retirement fails, the cleanup
workflow can be run manually for the closed pull-request number. A preview build is never promoted
to production.
user-facing preview URL. The `docs preview` commit status on the exact pull-request head links to
that URL after deployment and smoke checks succeed; pending, failed, or cancelled runs link to the
workflow logs. The workflow's automatic environment deployment record belongs to its trusted `main`
controller, so the explicit commit status provides the PR-facing entry point. Changes to this
`workflow_run` controller take effect after merging into `main`. Cloudflare retains the underlying
immutable deployments in its history. If automatic retirement fails, the cleanup workflow can be run
manually for the closed pull-request number. A preview build is never promoted to production.

Protected `main` is the publication authority. `Pages deployment` runs for a push to `main`; failed
runs can be rerun for the same commit, while rollback starts by reverting `main` through a pull
Expand Down
6 changes: 4 additions & 2 deletions website/content/en/developer/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ activity are separate states:
- **enabled**: a particular client is permitted to run it;
- **running**: the current runtime has started it and applied its side effects.

The lifecycle and ownership model exists, but a stable third-party package format and SDK guide are
not yet public contracts.
Core supports native Python wheels with versioned Registry releases and explicit Host compatibility.
The [Source Extension tutorial](/developer/ecosystem/source-extension) covers the current Core Host
0.2 path. Its Source programming interfaces still import Core modules; the delivery Toolkit is not a
standalone Source SDK or a promise of compatibility with every future Host.

### APIs

Expand Down
4 changes: 4 additions & 0 deletions website/content/en/developer/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ description: Route an InKCre change to its canonical owner and repository-specif
InKCre welcomes code, documentation, design, and ecosystem contributions. Start by identifying the
owner of the change; then follow that owner's current development and review contract.

If you want to connect another service without changing InKCre itself, use the
[ecosystem developer guide](/developer/ecosystem/) instead. Your Extension can live in your own
repository; it does not need to become a Core contribution.

> [!NOTE] There is no copied, universal setup or contribution procedure. Toolchains and commands
> belong to the repository that runs and verifies them.

Expand Down
Loading
Loading