Skip to content

Convert to monorepo with scoped packages - #8

Merged
huangyiirene merged 5 commits into
mainfrom
copilot/upgrade-to-multi-package-structure
Jan 18, 2026
Merged

huangyiirene merged 5 commits into
mainfrom
copilot/upgrade-to-multi-package-structure

Conversation

Copilot AI commented Jan 18, 2026 •

Copy link
Copy Markdown
Contributor

Restructure repository from single package to pnpm workspace with 5 scoped packages for granular consumption.

Package Structure

  • @objectstack/spec-meta - Metamodel types (ObjectEntity, ObjectField, ObjectView)
  • @objectstack/spec-plugin - Plugin runtime interfaces (ObjectStackPlugin, PluginContext)
  • @objectstack/spec-schemas - Zod validation schemas (ManifestSchema)
  • @objectstack/spec-constants - Path conventions (PKG_CONVENTIONS)
  • @objectstack/spec - Aggregator package re-exporting all sub-packages

Backward Compatibility

Existing imports remain unchanged:

// Still works
import { ObjectEntity, ManifestSchema, PKG_CONVENTIONS } from '@objectstack/spec';

// New: granular imports for smaller bundles
import { ObjectEntity } from '@objectstack/spec-meta';

Changes

  • Moved source files to packages/*/src/
  • Configured pnpm workspace with workspace:* dependencies
  • Updated tsconfig to base configuration pattern
  • Configured changesets with linked versioning for all packages
  • Added files field to package.json for publish control
Original prompt

升级为多包结构


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits January 18, 2026 12:08
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Copilot AI changed the title [WIP] Upgrade project to multi-package structure Convert to monorepo with scoped packages Jan 18, 2026
Copilot AI requested a review from huangyiirene January 18, 2026 12:15
@huangyiirene
huangyiirene marked this pull request as ready for review January 18, 2026 12:23
@huangyiirene
huangyiirene merged commit fde0b69 into main Jan 18, 2026
xuyushun441-sys pushed a commit that referenced this pull request May 22, 2026
…invariant)

User feedback: in shared-DB multi-tenant deployments, only render-time
metadata types (views, dashboards, reports) may be overridden per
organization. Anything that affects the physical schema, persisted
contracts, or automation guarantees must be platform-global — otherwise
a per-org overlay on, say, an object would diverge the table schema
that the whole tenant pool depends on.

This invariant is already enforced via MetadataTypeRegistryEntry.allowOrgOverride
in the spec, but two gaps remained:

  1. `report` had `allowOrgOverride: false` even though it is pure
     presentation — flipped to true to match the principle.
  2. The principle itself was not written down. Added a 'Tenant-
     customizable type whitelist' section to ADR-0005's amendment block
     with a complete table grouped by domain, plus the rule: 'default
     for any new metadata type is allowOrgOverride: false; promotion
     requires (a) overlay Zod schema, (b) written render-only rationale.'

No code change beyond the report flag — protocol.ts already derives
OVERLAY_ALLOWED_TYPES from the registry (Prime Directive #8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
xuyushun441-sys pushed a commit that referenced this pull request May 22, 2026
…PR-10a)

Pins down two invariants that PR-10b/c (LayeredRepository overlay
refactor) must preserve:

  1. Whitelist enforcement — only allowOrgOverride:true types may be
     saved as per-org overlays in project-kernel mode. Verifies:
       - 5 accepted (view, dashboard, report, email_template, plural form)
       - 11 denied with code='not_overridable', status=403
         (object, field, flow, workflow, agent, permission, role,
         profile, datasource, objects, flows)
       - Single-kernel mode (no projectId) bypasses the gate
       - Registry-as-source-of-truth: every allowed/denied type traces
         back to DEFAULT_METADATA_TYPE_REGISTRY.allowOrgOverride
         (Prime Directive #8 — no parallel allow-list)

  2. Canonical hash stability — precondition for backfilling _hash on
     existing sys_metadata rows in PR-10b. Verifies canonicalize/
     hashSpec are insensitive to key order, undefined-vs-absent, and
     deeply-nested optional fields; positional semantics preserved
     for arrays; distinct payloads → distinct hashes.

Adds @objectstack/metadata-core as a workspace dep of @objectstack/
objectql to import canonicalize/hashSpec.

Test count: objectql 258 → 286. All green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
xuyushun441-sys pushed a commit that referenced this pull request May 22, 2026
…etadata (ADR-0008 PR-10b)

PR-10b of ADR-0008. Wraps the existing sys_metadata table behind the
canonical MetadataRepository interface from @objectstack/metadata-core.
No production wiring yet — that lands in PR-10c when LayeredRepository
composes [FileSystemRepository, SysMetadataRepository] and the manager
bridge routes overlay PUTs through it.

What's implemented (M0 single-row update semantics):
  - get / put / delete against (type, name, organization_id) row
  - hashSpec stamping into _hash column (PR-10a guarantees stability)
  - optimistic locking: parentVersion must match HEAD _hash
  - no-op short-circuit on identical-body put (no event, no seq bump)
  - whitelist enforcement: refuses put/delete for types whose registry
    entry has allowOrgOverride: false (Prime Directive #8)
  - in-process watch() with type/name/org/branch filter
  - list() yields MetadataItemHeader (body stripped)
  - close() for clean teardown

Deferred to M1:
  - append-only metadata_events table + cross-replica seq
  - history() — currently yields empty
  - branch ops (fork/merge)
  - hashSpec backfill for legacy rows missing _hash

Tests: 16 new tests covering CRUD, optimistic locking, whitelist,
list, watch (incl. filter), history no-op, close. objectql 286 → 302.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
xuyushun441-sys pushed a commit that referenced this pull request May 29, 2026
Repoint email_template's canonical Zod schema from the legacy
EmailTemplateSchema (a sub-shape inside Notification) to
EmailTemplateDefinitionSchema across both runtime registries:

- packages/objectql/src/protocol.ts (TYPE_TO_SCHEMA → used by
  protocol.getMetaTypes() to emit JSON Schemas for Studio)
- packages/spec/src/kernel/metadata-type-schemas.ts
  (BUILTIN_METADATA_TYPE_SCHEMAS)

Resolves Prime Directive #8 (one canonical Zod source per metadata type)
for email_template. Studio property panel now renders all canonical
fields (name, label, category, locale, subject, bodyHtml, bodyText,
variables, fromOverride, replyTo, active, isSystem, description) with
no missing-schema warnings.

Also wires emailTemplates as a top-level defineStack() collection:
- MAP_SUPPORTED_FIELDS + PLURAL_TO_SINGULAR (spec/shared)
- ARTIFACT_FIELD_TO_TYPE (metadata plugin)
- APP_CATEGORY_KEYS (runtime app-plugin)

Rewrites the email-template FormView for the new shape and ships three
reference templates in examples/app-crm (crm.deal_won, crm.welcome,
crm.lead_followup) wired via emailTemplates: Object.values(emails).

Verified end-to-end: list, detail+preview iframe, property panel, and
GET /api/v1/meta all return the new schema.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…shared process table (objectstack-ai#17809)

Fixes objectstack-ai#17104

The shared-process-table rule is written for SIGNALS only.
`guard-process-kill.sh`'s ALLOWED
list ends by calling `pgrep -f` a harmless READ — correct about killing,
and exactly where the
card's measured trap sits. A name-matched WAIT has the same root cause
(one process table, text
matching, no ownership check) and the same property that makes the kill
rule a rule rather than
advice: **it reports success.** A `pgrep -f "$PAT"` run from a shell
whose own command line
contains `$PAT` matches the asker itself, `head -1` takes it, and the
wait never returns.

Two prose edits, no guard logic and no new refusal — per the grading, a
wait that never returns
spends only the asker's own budget, so this half is a written rule, not
a hook.

## Edit 1 — `AGENTS.md`, Prime Directive objectstack-ai#8 (the kill rule's line)

**Before** (lines 533–534, 89 + 113 = 202 bytes):

```
   the task is done** (`kill $(lsof -ti tcp:PORT)`). Don't leave orphan servers behind.
   ⛔ One process table per container: **kill only a PID you recorded, never a name** (`guard-process-kill.sh`).
```

**After** (lines 533–534, 113 + 116 = 229 bytes):

```
   the task is done** (`kill $(lsof -ti tcp:PORT)`). ⛔ One process table per container: **kill only a PID you
   recorded, and wait only on one, never a name** — `pgrep -f` matches the asking shell (`guard-process-kill.sh`).
```

File line count: **1075 before, 1075 after.** No ceiling raise.

### How the line was paid — and why the dispatch's first route is not
available

The dispatch offered three routes and noted the ratchet "pins a
widest-table-row byte count, not
prose width". That is true of `MAX_TABLE_ROW_BYTES` (AGENTS.md 768,
headroom 0, untouched here),
but it is not the whole ratchet: `check-skill-line-ratchet.mjs` also
carries a **flat
`MAX_LINE_BYTES = 120`** prose rule, and line 534 measured **113 bytes —
7 bytes of headroom**
against a clause that needs ~80. **Route (a), riding line 534, is
arithmetically impossible.**

Route (b) — folding into the paragraph's existing lines — is re-wrapping
slack into new content.
`os-dev.md` forbids that spelling explicitly (「付行数棘轮的唯一合法货币是删内容:⛔ 不拿
re-wrap
(折行合并)当筹行,新增以删减付账」), and the ratchet's own header names
widening-to-budget as the
defect it was built against. Flagging rather than silently choosing: the
dispatch offers a route
`os-dev.md` closes, and `os-dev.md` wins.

So route (c), **paid by deletion in the same edit**: `Don't leave orphan
servers behind.` (34
bytes including its leading space) is deleted. It loses nothing — it is
a negative restatement of
the bolded **`shut it down yourself when the task is done`** that
immediately precedes it in the
same sentence pair; the rule's positive form, its prescribed teardown
command and its scope all
survive untouched. No other content was removed.

Both new lines are within the 120-byte budget (113, 116). The wrap falls
inside a bold span,
which is this file's own established convention — lines 532→533 already
wrap inside
**`shut it down yourself when the task is done`**.

## Edit 2 — `.claude/hooks/guard-process-kill.sh` (header comment only)

Line 60's sentence **keeps its meaning about signals** and gains the
caveat (85 → 91 bytes, plus
an 88-byte continuation line):

```
#   pgrep -f foo / ps aux | grep node    — a READ. Nothing dies; look all you like. But a
#                                          READ is not a WAIT — see the section below.
```

A new header section then carries the positive form and the spelling
detail the AGENTS.md ceiling
could not hold (`cmd & pid=$!` then `wait "$pid"`, a pidfile, or a
wrapper that prints its own
PID), the measured five-hour self-deadlock, and the reason this half is
deliberately unguarded.
`.claude/hooks/` carries **no ceiling** (the ratchet header states this
explicitly), so the detail
belongs here — which is also the file's existing division of labour with
the kill rule.

File: 333 → 361 lines. New comment lines are ≤94 bytes, matching the
file's existing widest
comment line exactly. `bash -n` clean; hook still wired in
`.claude/settings.json` and still
`-rwxr-xr-x`.

## Verification

Final commit `35a20c6`.

**Governed verdict** — `node scripts/pm/check-governed-merges.mjs --test
AGENTS.md .claude/hooks/guard-process-kill.sh`, **exit 3**:

```
governed-surface predicate: 2 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
  ⛔  GOVERNED — a human merge is the review record for this PR (objectstack-ai#9495 regime).
      No seat flips it ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive objectstack-ai#14).
```

**Guard self-test** — `.claude/hooks/guard-process-kill.selftest.sh`:
**69 passed, 0 failed** both
before and after the edit (exit 0). It pins the *refusal* text, not the
header comment, so a
header-only change leaves it green and no pin needed updating.

**Ratchet** — `pnpm check:pm-skill-ratchet`, exit 0:

```
✓ check-skill-line-ratchet: AGENTS.md: widest table row is 768 bytes (pin 768; headroom 0).
✓ check-skill-line-ratchet: AGENTS.md is 1075 lines (ceiling 1075; headroom 0).
```

**Derived gate set** — `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`
derived 18 families from the 2-path change set; all 18 run, all exit 0.
Reconciled with `--ran`:

```
Run reconciliation — 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED
  (a DERIVED zero — all 18 recorded an exit code and none of them is 3).
```

Two gates first returned **exit 3 = PREREQUISITE NOT MET** (not a
finding): `node_modules` was
absent until `pnpm install`, and `check:doc-formula-expressions`
additionally needed
`@objectstack/formula` and `@objectstack/lint` built. Both re-ran at
exit 0 after the builds
(`os-verify-lock.sh`, `VERDICT command-exit 0`).

**Control characters** — `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'`
over both files: no
matches. `pnpm check:nul-bytes` exit 0.

**objectui pin check** — read from `objectui`
`origin/main`:`scripts/upstream-port-pin.json`.
It pins **11** upstream paths (the `guard-main-checkout*`,
`guard-shared-stash*` and
`guard-tree-enum*` families plus `check-half-states.mjs`,
`invoked-as.mjs`,
`check-bash32-floor.mjs`). `guard-process-kill` appears **zero** times.
**No downstream pin
moves**, and the objectui parity check has nothing to flag.

**changeset**: `skip-changeset` — `AGENTS.md` (repo-root instruction
file) and `.claude/**` are
both on the fast track; nothing in any package's `files[]` moves.

## 维护者速读(草稿)

**改了什么** —— 两处纯文字改动。`AGENTS.md` 第 534 行的「只杀你记录过的 PID,绝不按名字杀」规则,
补上同样正向形式的另一半:**等待也只等你记录过的 PID**;`guard-process-kill.sh` 头部注释里那句
「`pgrep -f` 只是读,随便看」保留它关于「信号」的原意,并补上关于「等待」的告诫。⛔ 没有改任何守卫
逻辑,没有新增拦截。

**为什么改** —— 卡片记录的是一次实测事故:三个后台等待进程死锁了五个小时。原因是 `pgrep -f "$PAT"`
在一个自身命令行就包含 `$PAT` 的 shell 里执行,匹配到了**提问者自己**,于是 `tail --pid` 在等自己的
父进程、父进程在等它。这个错误**会报成功** —— `pgrep` 返回 0,还打印出一个看起来很合理的 PID,所以
没有任何信号提示出错。杀错进程毁的是别人的活、动静很大;等错 PID 静悄悄地烧掉的是你自己的时间,所以
它能跑满五个小时都没人发现。

**风险与代价(含回滚)** —— 风险极低:改的是说明文字,不是可执行逻辑。`bash -n` 通过,守卫自检
69 项全绿(改动前后一致),钩子仍然挂载且可执行。代价是 `AGENTS.md` 行数棘轮在 1075/1075 零余量,
为了不抬上限,删掉了「Don't leave orphan servers behind.」这句话——它只是前面加粗那句「任务做完自己
关掉」的反面重复,规则内容没有任何损失。回滚就是 `git revert` 这一个 commit,无迁移、无依赖。

**席位意见** —— (留空,待 skills 席位定稿)

**你要做的** —— 这是受管面(`AGENTS.md` + `.claude/**`),按 Prime Directive objectstack-ai#14
由维护者**手动合并**;
⛔ 不翻 ready、不进合并队列、不开 auto-merge。请确认一点:用「删掉那句重复话」来换这一行,是否认可
——若您认为那句话应当保留,替代方案是抬 `AGENTS.md` 的行数上限(需要您的裁决),我没有自行抬。

## Acceptance notes

Out of scope, noted and **not** filed (neither is a reproducible defect,
a declared-contract
violation, nor a metadata trap — the three filing classes):

- The dispatch's reading that the ratchet "pins a widest-table-row byte
count, not prose width"
is incomplete: a flat `MAX_LINE_BYTES = 120` prose rule also applies,
and it is what caps line
534. Reported to the PM rather than filed — it is a fact about this
dispatch's premise, already
  corrected above. Carrier: the skills seat, on this PR.
- The dispatch's "AGENTS.md prose lines wrap at ~100 columns in that
section" does not match the
measurement: Prime Directive objectstack-ai#8 wraps at 84–90 bytes, and only the ⛔
line ran to 113. Same
  carrier, same PR; no separate card.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…used to be (objectstack-ai#17838)

The Protocol Domains table in `AGENTS.md` still listed a `Cloud` /
`cloud/` row after the cloud control-plane contracts left the
open-source spec and the package & marketplace format moved to
`packages/spec/src/marketplace/`, published as
`@objectstack/spec/marketplace`. The row sent a reader to a directory
the tree no longer has, and omitted the one that replaced it. This
replaces that one row with a `Marketplace` / `marketplace/` row at the
same alphabetical position (after `Kernel`), described from the
directory's actual modules rather than from the card's proposal. One
line in, one line out — 1075 lines before and after. Docs-only,
`skip-changeset`.

Fixes objectstack-ai#17370

## Acceptance notes

### The row

```
-| `Cloud` | `cloud/` | Multi-tenant, deployment, environment |
+| `Marketplace` | `marketplace/` | Package, Version, Listing, Install, Template |
```

### Premise findings, re-taken on `origin/main` `51b024a16` (the PM took
them on `d79f2499`; `main` advanced by two `docs(pm)` commits, objectstack-ai#17828
and objectstack-ai#17823, neither touching `AGENTS.md`)

1. **Holds.** `AGENTS.md:657` read `| `Cloud` | `cloud/` | Multi-tenant,
deployment, environment |` directly after the `Kernel` row at `:656`;
the file is 1075 lines.
2. **Holds.** `git ls-tree -d origin/main packages/spec/src/marketplace`
answers `040000 tree 3f19482…`; `packages/spec/src/cloud` answers
nothing. `packages/spec/package.json` exports `./marketplace`, and has
no `./cloud`.
3. **Holds.** `git grep -ln "Multi-tenant, deployment, environment"
origin/main` hits only `AGENTS.md`; `git grep -ln "Protocol Domains"
origin/main -- scripts .github packages/spec/scripts` hits nothing. No
gate reads this table.
4. **Holds, re-measured.** All 22 open PRs' file lists scanned at this
commit, fully paginated (PR objectstack-ai#17076 is the standing Version Packages PR
at 383 changed files — four pages read). None touches `AGENTS.md`.
5. **FALSIFIED — the dispatch's line-width premise.** The dispatch named
"AGENTS.md's own line-width rule, Prime Directive objectstack-ai#8" wrapping at ~84–90
bytes. There is no such rule. Prime Directive objectstack-ai#8 is **North Star
alignment**; `AGENTS.md` carries no line-width rule at all. The real
120-byte budget is `MAX_LINE_BYTES` in
`scripts/pm/check-skill-line-ratchet.mjs`, and it **exempts a markdown
table row by shape** (`EXEMPTION_CLASSES.table`: "a wrapped `|` row is a
different table"). What actually binds a table row is that script's
**second** ratchet, `MAX_TABLE_ROW_BYTES`, pinned for `AGENTS.md` at
**768 bytes**, shrink-only, headroom 0. The new row is far under it, and
the gate's own verdict lines confirm both pins are untouched:

   ```
✓ check-skill-line-ratchet: AGENTS.md: widest table row is 768 bytes
(pin 768; headroom 0).
✓ check-skill-line-ratchet: AGENTS.md is 1075 lines (ceiling 1075;
headroom 0).
   ```

The dispatch's *operative* instruction — stay within the table's own
widest row — was honoured anyway; see the width note below.

### Work item B — other `cloud/` references in AGENTS.md

`git grep -n "cloud/" origin/main -- AGENTS.md` returns exactly one hit:
line 657, the row being replaced. Nothing else in the file to report,
and nothing else was touched.

### The description, verified against the directory

`git ls-tree -r --name-only origin/main packages/spec/src/marketplace`
lists ten files — five modules and five test files:

```
index.ts
marketplace.zod.ts          marketplace.test.ts
package.zod.ts              package-namespace.test.ts
package-version.zod.ts      package-l10n.test.ts
package-l10n.ts             template-manifest-id.test.ts
template-manifest.zod.ts
```

The card proposed 「Package identity & versions, marketplace listing /
publish / install format, template manifests」. That description is
**accurate** — it does not overstate — but it is 132 bytes as a row,
against a table whose widest row is 84, and its `&` / ` / ` punctuation
is out of register with its neighbours, which are bare comma-separated
noun lists of the domain's principal schemas (`Data` → "Object, Field,
FieldType, Query, Filter, Sort"; `Security` → "Permission, Role,
Policy"). So it was shortened into that register, one noun per real
schema family, each traceable to a module's top-level exports:

| Noun | Module | Principal exports |
|:---|:---|:---|
| `Package` | `package.zod.ts` | `PackageSchema`,
`PackageTranslationsSchema`, `CreatePackageRequestSchema` |
| `Version` | `package-version.zod.ts` | `PackageVersionSchema`,
`PackageManifestSchema`, `PublishPackageVersionRequestSchema` |
| `Listing` | `marketplace.zod.ts` | `MarketplaceListingSchema`,
`PackageSubmissionSchema`, `MarketplaceSearchRequestSchema` |
| `Install` | `marketplace.zod.ts` | `MarketplaceInstallRequestSchema`,
`MarketplaceInstallResponseSchema` |
| `Template` | `template-manifest.zod.ts` | `TemplateManifestSchema` —
the on-disk `objectstack.manifest.json` descriptor |

`package-l10n.ts` is the listing-localisation resolver over
`PackageTranslations` and folds under `Package`; the neighbouring rows
do not enumerate helper resolvers either (`Shared` → "Error maps,
normalization utilities").

### Byte width of the new row versus the table's widest

| Row | Bytes |
|:---|---:|
| New `Marketplace` row | **81** |
| Table's widest row — `UI` (`:649`) | 84 |
| Old `Cloud` row (replaced) | 62 |
| `MAX_TABLE_ROW_BYTES` pin for AGENTS.md (the gate) | 768 |

The new row is 3 bytes under the table's own widest and 687 under the
enforced pin.

### Gates

Derived in this worktree with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` (no paths — the tool takes
its own change set from the merge base). 14 families derived, **14 run,
all exit 0**:

```
node scripts/check-closing-keyword-parity.mjs             :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs                :: exit 0
pnpm check:agent-test-spelling                            :: exit 0
pnpm check:docs-audit-scope                               :: exit 0
pnpm check:driver-memory-census                           :: exit 0
pnpm check:nul-bytes                                      :: exit 0
pnpm check:pm-governed-merges                             :: exit 0
pnpm check:pm-governed-prose                              :: exit 0
pnpm check:pm-skill-id-lint                               :: exit 0
pnpm check:pm-skill-ratchet                               :: exit 0
pnpm check:refd-timer-probe                               :: exit 0
pnpm check:required-contexts                              :: exit 0
pnpm check:watch-hint-literal                             :: exit 0
```

Reconciled: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --ran RANFILE` (exit codes recorded per
family) →

```
✓ dispatch-gates --ran: 14 derived famil(ies) accounted for — 14 run, 0 NOT-MEASURED
  (a DERIVED zero — all 14 recorded an exit code and none of them is 3).
```

All gate readings above were taken at `f68b09653`, the final commit on
this branch.

No build or test family is owed: the diff touches no package, so the
closure build is empty and no package's `pnpm test` / `pnpm typecheck`
is in scope. Control-byte self-scan beyond `check:nul-bytes`: `grep -naP
'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' AGENTS.md` exits 1 with zero output.

### Changeset

`skip-changeset`. Measured, not assumed: of the 76 tracked package
manifests, zero published package lists anything matching `AGENTS` in
its `files[]`, against a positive control of 70 published packages that
do list `README`. `AGENTS.md` is a repo-root instruction file and ships
in no tarball, so this PR publishes nothing.

### Governed surface

`AGENTS.md` is a governed surface (Prime Directive objectstack-ai#14). This PR stays a
**draft**; it is never flipped ready, never queued, and auto-merge is
never armed by this seat. The PM places the four-piece and the
maintainer merges.

## 维护者速读(草稿)

**改了什么** — `AGENTS.md` 的 Protocol Domains 表里那一行 `Cloud` / `cloud/` 换成了
`Marketplace` / `marketplace/`,位置不变(仍在 `Kernel`
之后,按字母序),描述按目录里实际有的五个模块重写。整个 PR 就这一行,进一行出一行,文件 1075 行前后不变。

**为什么改** — objectstack-ai#16325 把云控制面的协议移出开源 spec(option B「cut by
owner」,维护者「同意」),包与市场格式搬到了
`packages/spec/src/marketplace/`。那一行从此指着一个不存在的目录,并且漏掉了顶替它的那个。没有任何门禁读这张表,所以它只会一直烂着,不会变红——这正是要人来修的原因。

**风险与代价(含回滚)** — 风险接近于零:纯文档一行,不触碰任何包,不发布任何东西,14 个派生门禁全绿。回滚就是把这一行改回去,一次
revert 即可,没有任何下游依赖。唯一需要留意的是这是受管面,必须由维护者手工合并。

**席位意见** — (留空,待席位定稿)

**你要做的** — 确认 `Marketplace` 行的描述用词是否合你的意:目前是 `Package, Version, Listing,
Install, Template`,比卡片原提案短,理由是要和表里其他行同一种写法(裸名词列表)、同一个宽度量级(81 字节,表内最宽
84)。如果你更想保留卡片原来那句更长的描述,告诉我,我改。确认后由你手工合并。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK)_

Co-authored-by: Claude <noreply@anthropic.com>
os-zhuang pushed a commit that referenced this pull request Sep 18, 2026
Fixes #19131

Clause-②: no

One new file — `docs/NORTH-STAR.md` — carrying the maintainer's North
Star text exactly as finalized in chat on 2026-09-18 (「同意,北极星定稿」 ·
「其他同意」). Nothing else moves: the pointers in `AGENTS.md` / `SKILL.md` /
`PRIORITIZATION.md` and the governed register belong to #18489, which
remains open.

Governed by intent — this page is meant to sit above `AGENTS.md` as the
fleet's law. Draft on purpose: the maintainer merges it by hand. No seat
readies it, queues it, or arms auto-merge on it.

## Byte-for-byte proof

The file's content is the fenced markdown block of #19131's body,
extracted mechanically and compared byte for byte. Card body read
through the REST proxy (4646 bytes); the extracted block is 3271 bytes
over 25 lines, with no trailing whitespace on any line and exactly one
terminating newline.

````text
# 1 — read the card body through the REST proxy
curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" \
     -H "Accept: application/vnd.github+json" \
     https://api.github.com/repos/objectstack-ai/objectstack/issues/19131 > card.json
node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync("card.json","utf8")).body)' > card-body.md

# 2 — extract the fenced block, dropping both fence lines
sed -n '/^```markdown$/,/^```$/p' card-body.md | sed '1d;$d' > card-block.md

# 3 — compare
$ diff card-block.md docs/NORTH-STAR.md
$ echo $?
0

$ sha256sum card-block.md docs/NORTH-STAR.md
b94152a07b9fd81aa59e019875aa5a53a00a7c058e53e098bb1b6f65ac6f67a8  card-block.md
b94152a07b9fd81aa59e019875aa5a53a00a7c058e53e098bb1b6f65ac6f67a8  docs/NORTH-STAR.md

$ git show HEAD:docs/NORTH-STAR.md | sha256sum
b94152a07b9fd81aa59e019875aa5a53a00a7c058e53e098bb1b6f65ac6f67a8  -
````

Two independent extractions were run — the `sed` range above and a
JSON-side split on the fence lines — and both produce the same sha256,
so the proof does not rest on one parser. The card body itself was
checked for sanitizer damage first: exactly one opening fence and one
closing fence, no image-bracket sequence, complete Markdown.

Nothing was reworded, reformatted, translated or annotated: no added
heading, date, number, provenance line, status line, table of contents
or footer, and no trailing-whitespace "fix".

## Gates

Derived, not guessed: `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` (change set `docs/NORTH-STAR.md`, one
path, against merge base `14a762f9f`). All 11 derived families were run
at `37f1830fd`; every exit code captured redirect-then-`$?`, never
through a pipe.

| command | exit |
|:---|:---|
| `node scripts/check-ci-filter-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 |
| `pnpm check:cross-package-test-inputs` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:watch-hint-literal` | 0 |

Reconciled with `dispatch-gates --ran`: *11 derived families accounted
for — 11 run, 0 NOT-MEASURED (a DERIVED zero — all 11 recorded an exit
code and none of them is 3)*.

Two readings worth naming:

- `check:doc-authoring` is the gate that actually reads this file: its
roots are `.claude`, `docs`, `skills`, `content`, so a hand-written
top-level guide under `docs/` is corpus. It reports *402 files clean —
no bare metadata literals*.
- `check:doc-formula-expressions` first exited **3** (PREREQUISITE NOT
MET — `@objectstack/lint` resolves to source but its compiled entry
point was absent). That is not a finding and measures nothing.
`@objectstack/formula` and `@objectstack/lint` were built through
`scripts/pm/os-verify-lock.sh` (VERDICT `command-exit 0`, both runs),
after which the gate passed: *22 record-scoped formula examples across
439 files / 1378 TS blocks judged clean*.

Outside the derivation, one extra docs audit was run for this doc-only
diff: `node scripts/docs-audit/check-affected-docs.mjs` :: exit 0.

No package build or package test is owed: `pnpm-workspace.yaml`'s globs
cover `packages/*`, its seven nested roots, `apps/*` and `examples/*` —
none of them reaches repo-root `docs/`, so this path belongs to no
workspace package and there is no affected set to build or test.

## Repo-wide lint, narrowed — with the three readings the narrowing
needs

Instead of the farm-wide `eslint . --no-inline-config`, this diff's lint
obligation is empty, and that is a measurement rather than a skip:

1. **Population, read off eslint's own config, not guessed:** every
`files:` entry in `eslint.config.mjs` targets
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` or a narrower subset of those
extensions. No config object matches Markdown.
2. **File count, read off `--format json`:** `pnpm exec eslint
--no-inline-config --format json docs/NORTH-STAR.md` returns one entry
with `errorCount: 0` and the single message *"File ignored because no
matching configuration was supplied."* — the diff contributes **0**
linted files.
3. **Invariance for untouched files:** `eslint.config.mjs` is not in
this diff, and this repo runs one config that never enables type-aware
linting for any file (no `parserOptions.project`, no typed rules —
stated in the config's own header comment and true of every
`languageOptions` block, each carrying only `ecmaVersion` and
`sourceType`). A Markdown file outside the lint population therefore
cannot move the verdict on any file this diff does not touch.

Readings 1–3 were taken at `37f1830fd`, the final commit. `turbo ls
--affected` is deliberately **not** cited: with no base to compare
against in a fresh worktree it printed all 81 packages, which is a
fallback, not a reading.

## Changeset

`skip-changeset`, measured rather than assumed: an npm tarball can only
contain files inside its own package directory, and `docs/NORTH-STAR.md`
sits outside every workspace package (globs above). Cross-checked from
the other side — no package manifest in `packages/**`, `apps/**`,
`examples/**` or `skills/**` has a `files[]` entry referencing a `docs/`
path — and the repo-root manifest is `private: true`. Nothing published
moves, so there is no released surface for a changeset to describe.

## Acceptance notes

- *noted, not filed:* `AGENTS.md` Prime Directive #8 still sends readers
to `content/docs/concepts/north-star.mdx`, so after this lands two
north-star documents coexist until the pointers are rewired. That
rewiring — AGENTS.md, the skill files, `PRIORITIZATION.md` and the
governed register — is explicitly the other card's scope, and #18489
remains open as its carrier. Not filed as a defect: nothing is broken
today, and the successor is already queued.

## 维护者速读(草稿)

**改了什么**:新增一个文件 `docs/NORTH-STAR.md`,内容就是你 2026-09-18
在聊天里定稿的那段北极星文本,逐字节照搬(sha256 与卡面 fenced block 一致,diff 为空)。没有第二个文件改动。


**为什么改**:把这段定稿从聊天记录落成仓库里的一页,让它能被引用、被门禁读到、被后续的指针改动挂上。文本本身没有任何加工——没加标题、日期、编号、来源行、状态行、目录或页脚。

**风险与代价(含回滚)**:风险接近零——这是一页文档,不改任何代码或已发布产物,`skip-changeset`
已实测(不在任何包目录内)。回滚就是删这一个文件,无迁移、无依赖。唯一的暂态是:在 #18489 把指针改完之前,`AGENTS.md` 第 8
条仍指向旧的 `content/docs/concepts/north-star.mdx`,两页会并存一段时间。

**席位意见**:(留空,待席位定稿)


**你要做的**:读这一页,不同意的地方**直接删行或改行**——这是你的文本,不是我的草案,所以我没有替你改任何一个字。定稿后**由你手动合并**(按设计留作
draft:这页是 `AGENTS.md` 之上的法,没有任何 AI 席位会 ready、入队或武装它)。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants