You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 7c01d30
Browse filesBrowse the repository at this point in the historyBrowse files
#23250 moved two counts in the generated tenant-audit census page and its
counts ledger. Both took main's side here to commit the merge; the next
commit regenerates them from the merged tree.
Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
feat(create-objectstack): a scaffolded project installs its AI skills from the `@objectstack/skills` package at its own `@objectstack/spec` version, never from the repository's `main` branch
6
+
7
+
Clause-②: no
8
+
9
+
-**The project depends on the catalog.** The blank template's `devDependencies` gain `@objectstack/skills`, at the same `^<version>` as every other `@objectstack/*` dependency, so a new project's dependency set changes: the skills are the ones written for the `@objectstack/spec` it installs.
10
+
-**The install reads `node_modules`.** After `<pm> install`, the scaffolder runs `npx -y skills@1.7.2 experimental_sync --agent claude-code --copy -y`, which copies the installed package's catalog into `.claude/skills/` as real files, once, beside a `skills-lock.json` that names the package as every skill's source. The GitHub `npx skills add objectstack-ai/objectstack/skills …` command is gone from the run, its summary and the `AGENTS.md` and `README.md` it writes into the project.
11
+
-**The summary says what happened.** It names the package version installed and prints the same command for re-installing after an upgrade of the `@objectstack/*` packages, and for any other agent runtime. When the install leaves no catalog to read, or a skill did not come from the package, the run says so instead of reporting the skills installed.
12
+
-**The flags keep their meaning.**`--skip-skills` skips the skills step. `--skip-install` installs no skills, because nothing is in `node_modules` yet; the summary names `<pm> install` and then the sync command.
13
+
-**To move a project created earlier onto the package:** add `@objectstack/skills` to `devDependencies` at your `@objectstack/spec` range and run your package manager's install. Remove the skills `skills add` installed — `npx skills remove objectstack-data -y` and so on for each `objectstack-*` skill, or `npx skills remove --all` if you installed no others — because the sync skips a skill it did not install and leaves the old copy in place. Then run `npx skills@1.7.2 experimental_sync --agent claude-code --copy -y` (name your own runtime after `--agent`).
Inbound mail has an ingest seam: `MailIngestService`, the kernel service `mailbox`, turns one raw RFC 822 message into one `sys_mail_message` row, associates it at ingest with the records whose `mailAssociation` email fields hold a participant's address, and re-matches a mailbox on request
6
+
7
+
Clause-②: yes (the package gains an exported ingest entry point it does not export today; a widening of the public surface)
8
+
9
+
-**`ingest({ mailboxId, raw, receivedAt? }, context)`** reads one message and answers `stored`, `duplicate` or `suppressed`. Only the system context may call it: a message enters a mailbox from its mail source, never from a user.
10
+
-**Participants.** The addresses of From, Sender, Reply-To, To and Cc, plus the same five headers of a message forwarded as an attachment (a `message/rfc822` part of the received message). The mailbox's own address is left out. The set is stored on the row in `match_addresses`, a JSON list of `{ address, source }` where `source` is `outer` or `attachment`. A forward quoted inline in the body is not read yet, and `inline` is declared so a later reader of it can store its addresses.
11
+
-**Association.** The message is linked, one `sys_mail_association` row each (`matched_by: 'ingest'`), to every record of every object with an `email` field declaring `mailAssociation: true` whose value equals a participant address (ASCII case-insensitive), inside the mailbox's organization only. A flagged object with no organization column is skipped and logged once. No app code and no resolver service is involved. A message nothing matches is stored `association_state: 'unassociated'`: the restricted pool.
12
+
-**Dedupe.** One row per message per mailbox. The key is `mid:` plus the SHA-256 of the Message-ID, or `hdr:` plus the SHA-256 of the header block as received for a message without one. A second fetch answers `duplicate` and writes nothing, and the unique key settles a concurrent pair.
13
+
-**Threading.**`message_id`, `in_reply_to` and `reference_ids` hold the RFC 5322 ids with their angle brackets stripped.
14
+
-**Suppression.** No row is written for an auto-reply (`Auto-Submitted` other than `no`, `Precedence: bulk`, `junk` or `list`, `X-Autoreply` or `X-Autorespond` other than `no`) or a bounce (`multipart/report; report-type=delivery-status`).
15
+
-**`rematch({ mailboxId, messageIds? }, context)`** replays each message's stored participant set against the records that exist now. It adds the missing links (`matched_by: 'rematch'`) and moves a pool message to `associated` once it has one. It never removes a link. A stored set that is not the stored shape is reported in `unreadable` and not matched. The system context may run it, and so may the mailbox's owner and a `manage_mailboxes` holder in the mailbox's organization. Anyone else is refused with `PERMISSION_DENIED` (403), and another organization's mailbox answers `RECORD_NOT_FOUND` (404).
16
+
-**New exports:**`MailIngestService`, `MAILBOX_SERVICE`, `MailboxServiceError` (`code` and `status`), `MAIL_MATCH_ADDRESS_SOURCES`, `MAIL_SUPPRESSION_REASONS`, and the types `MailIngestInput`, `MailIngestResult`, `MailRematchInput`, `MailRematchResult`, `MailboxCallerContext`, `MailIngestEngine`, `MailIngestLogger`, `MailAssociationMatch`, `MailMatchAddress`, `MailMatchAddressSource`, `MailSuppressionReason`, `MailboxServiceErrorCode` and `RawMailMessage`. `MailboxServicePlugin` registers the service in `init()`.
17
+
-**New dependencies:**`postal-mime` (MIT-0, no dependencies of its own, ES module and CommonJS builds) reads the MIME structure, the encoded words and the address lists. `@objectstack/types`, already installed through `@objectstack/core`, supplies the keyset walk the re-match pages with.
18
+
-**Not yet:** no mail source feeds the seam (the IMAP connector ships separately), and attachments are not stored, so a message forwarded as an attachment keeps the forwarder's own text and not the forwarded message's body.
if [ ! -f node_modules/@objectstack/cli/package.json ]; then
@@ -377,6 +382,58 @@ jobs:
377
382
fi
378
383
echo "pinned as expected: FROM tag == resolved CLI ($RESOLVED)"
379
384
385
+
- name: 'Skills: the scaffolder installed exactly the @objectstack/skills catalog'
386
+
# The pinned-shape project above was scaffolded the way a real user
387
+
# does it, skills included. The scaffolder syncs the catalog out of the
388
+
# @objectstack/skills package its own install put in node_modules — at
389
+
# the range it gives every @objectstack/* dependency, so the version of
390
+
# @objectstack/spec — into .claude/skills/ as real files (the measured
391
+
# shapes live in packages/create-objectstack/src/skills-install.ts).
392
+
# So the catalog this step compares against is the package's, read off
393
+
# the registry-installed node_modules, never this checkout's skills/:
394
+
# the same skill names, the same count, copied rather than linked.
395
+
#
396
+
# The unpublished-version window degrades the way the pinned-shape step
397
+
# does, and only that window: with nothing installed at all, that step
398
+
# has already classified the registry answer and failed on any other
399
+
# cause, so this step asserts the degraded shape — no skills from
400
+
# anywhere — instead of nothing. An install that resolved the CLI but
401
+
# no @objectstack/skills is a template that lost its dependency: red.
402
+
run: |
403
+
cd "$RUNNER_TEMP/pinned-probe"
404
+
PKG=node_modules/@objectstack/skills
405
+
if [ ! -f "$PKG/package.json" ]; then
406
+
if [ -f node_modules/@objectstack/cli/package.json ]; then
407
+
echo "::error::the scaffolder's install resolved @objectstack/cli but no @objectstack/skills, so this project got no skills catalog at its own version — the template no longer depends on the package"
408
+
exit 1
409
+
fi
410
+
if [ -e .claude/skills ]; then
411
+
echo "::error::nothing was installed, yet .claude/skills exists — the scaffolder installed skills from a source other than the @objectstack/skills package"
412
+
exit 1
413
+
fi
414
+
echo "::warning::no @objectstack/skills is installed — the unpublished-version window the pinned-shape step classified. Catalog EQUALITY is SKIPPED for this run; the degraded shape (no skills from any source) was asserted."
415
+
exit 0
416
+
fi
417
+
418
+
skills_in() { for d in "$1"/*/; do [ -f "${d}SKILL.md" ] && [ ! -L "${d%/}" ] && basename "$d"; done | sort; }
Copy file name to clipboardExpand all lines: content/docs/permissions/system-context.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -170,6 +170,7 @@ One of the largest consumers of the flag;
170
170
| 45 | Comment access hooks return early (insert + update + delete, and the read AST), and so do the activity and audit-log read gates (their read AST), the activity field redaction and the audit-log field redaction (the rows a read serves), and the query guard over both objects' value-bearing columns (the read AST) | plugin-audit | Get: the whole activity row and the whole `sys_audit_log` row, its before/after snapshots included, on `find` / `findOne` — the audit writer, each read gate's own pre-scan and the activity redaction's — and a filter, sort or grouping by those columns. Lose: comment visibility scoping, the narrowing of `sys_activity` and of `sys_audit_log` to rows whose parent record the caller can read, the redaction of a parent field's value from an activity row's text and recorded change and from a ledger row's before/after snapshots, the withholding of an activity row recording an update whose every changed field the reader is withheld (on `count` and `aggregate` too, so a total agrees with the rows), and the refusal of a query over those columns for a reader withheld a field of the objects it can reach | `packages/plugins/plugin-audit/src/comment-access-hooks.ts#installCommentAccessHooks`, `#installCommentReadVisibility`, `packages/plugins/plugin-audit/src/activity-read-visibility.ts#installActivityReadVisibility`, `packages/plugins/plugin-audit/src/audit-log-read-visibility.ts#installAuditLogReadVisibility`, `packages/plugins/plugin-audit/src/activity-field-redaction.ts#installActivityFieldRedaction`, `packages/plugins/plugin-audit/src/audit-log-field-redaction.ts#installAuditLogFieldRedaction`, `packages/plugins/plugin-audit/src/parent-field-query-guard.ts#installParentFieldQueryGuard` |
171
171
| 46 | Knowledge search returns hits unfiltered | service-knowledge | Lose: the permission filter over search results |`packages/services/service-knowledge/src/knowledge-service.ts#applyPermissionFilter`|
172
172
| 47 | The inbound-mail read gates return early (the read AST) | service-mailbox | Get: every `sys_mail_message` and `sys_mail_association` row on `find` / `findOne` — the ingest writer and each gate's own pre-scan, which read the association and mailbox rows a decision needs. Lose: the narrowing of a message to callers who can read a record it is associated with or who own its mailbox (so the unassociated pool is restricted), and of an association to callers who can read the record it names (on `count` and `aggregate` too, so a total agrees with the rows) |`packages/services/service-mailbox/src/mail-read-visibility.ts#gateMiddleware`|
173
+
| 47b | The inbound-mail ingest seam: `ingest` admits the system context ALONE, and `rematch` admits it without the organization, owner or `manage_mailboxes` check | service-mailbox | Get: a mail source hands a mailbox its messages, and server code re-matches any mailbox. Lose: nothing a user held — every other caller of `ingest` is refused (`PERMISSION_DENIED`), because an ingested message is shown on every record its correspondents' addresses match and a user who could hand the seam a message could write one in a correspondent's name; every other caller of `rematch` must be in the mailbox's organization and own the mailbox or hold `manage_mailboxes`|`packages/services/service-mailbox/src/ingest/mail-ingest-service.ts#ingest`, `#authorizeRematch`|
173
174
174
175
### 5. Actions, metadata plane, provenance, the organization wall
0 commit comments