Skip to content

fix(plugin-sharing, plugin-audit): runtime strings state each decision in words instead of a tracker number (stage 6) - #21533

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20751-services-strings-stage6
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20751-services-strings-stage6

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20751
Clause-②: no

Stage 6 of the domain:services lane under the maintainer's A / A ruling (5902360492): the plugin-sharing strings and the one plugin-audit string left over from stage 4. The card stays open for the later stages, so this PR carries no closing keyword. Text only: no status, error code, field, route, export or control flow moves (the AST skeleton reads SAME for 6 of 6 changed .ts files, below).

What this does

Eight strings in these two packages sent the reader to a tracker number for the reason behind them. In form D, as stages 1 to 5 applied it, the number goes. Where the sentence already said what was decided, only the citation goes. Where it leaned on the number, it now says the decision in words.

All 8 ledgered occurrences in this stage's surface (claim 5962584533), re-derived from the ledger on origin/main at aa463223 and read again, the same eight, on ad7c3518 before merging: plugin-sharing 7 (7 pairs, 3 files: share-link-service.ts, sharing-rule-service.ts, sharing-service.ts) and plugin-audit 1 (audit-writers.ts, the entry stage 4 left for a later stage). The file excluded at dispatch, plugin-sharing/src/translations/serving-seam.test.ts (PR #21480, since merged), carries no ledger entry and is not touched by this diff.

Rewritten in words

Author- and administrator-visible text first, log lines last. Line numbers are at the head.

Where Cited The text now says Decision read from
plugin-audit audit-writers.ts:972-976, the missing-table fix in the audit write-failure line 5226 "so on a fresh os dev boot the table exists in that sibling file and not in the primary one; look there before concluding it was never created" 5226's dev measurement and the PM review: the "table never created" premise was falsified; on a fresh os dev boot sys_audit_log was in the sibling dev.telemetry.db (with its rows) and absent from dev.db, because ADR-0057 section 3.6 routes the audit lifecycle class to the telemetry datasource
sharing-service.ts:312, the reason the record-share orphan sweep prints in its "revoked N rows" line 5103 "every share on a deleted record goes, whatever its source, so a reused record id cannot inherit it" the ruling on 5103 (option A): a record's deletion revokes every sys_record_share row on it, whatever the source, plus a boot sweep by record existence; the card's hazard is a reused record id handing the old record's shares to the new one
share-link-service.ts:67, the same reason for the share-link sweep 5190 "a share link is a bearer token, so a reused record id must not inherit it" 5190's case and ACCEPT: a share link is an identity-less capability token, so on a reused id whoever holds it wins; a deleted record's links are revoked and resolution fails closed
sharing-service.ts:735-741, the write-gate failure log line (error) 6428 "a failed lookup is a refusal, never an abstention, because an abstention would hand the row to the other write authorities, which may admit it" 6428's tri-state write verdict (allow / abstain / deny), added because a two-state answer that merged "allow" with "no opinion" was measured failing open, and its fail-closed rule: a query failure is a deny, never an abstain (an abstain hands the row to another authority, a deny ends it)
sharing-service.ts:955-961, the authored-row-write probe log line (warn) 5493 "(fail-closed: only an app-authored row-level policy that positively admits this row may lift the sharing refusal)" the maintainer's ruling on 5493 (Q1 = A, Q2 = A1): the by-id write gate defers only on an admit from an app-authored, non-floor policy; abstain, a missing method or a throw leaves the refusal byte-for-byte
sharing-service.ts:1835-1842, the hierarchy-scope log line (warn) 5973 ""No org" is not "every org": the IHierarchyScopeResolver.resolveOwnerIds contract makes a resolver fail closed on a missing organization" 5973 is a pull request (merged as abeb3751f; its REST endpoints answer 404, its timeline reads): it made organizationId the authoritative, required field and wrote the fail-closed rule into IHierarchyScopeResolver.resolveOwnerIds

Citation only (the sentence already stated the decision)

  • sharing-rule-service.ts:424-429, the no-active-organization refusal (8158: "manage_sharing is an ORG-scoped capability ... answering unscoped would expose every tenant's rules. Select an active organization and retry. Platform operators ... and system contexts are unaffected"). Its PERMISSION_DENIED prefix is unchanged.
  • sharing-rule-service.ts:529-533, the platform-global delete refusal (7795: "requires platform authority ... Org-scoped manage_sharing does not authorize it, because this rule belongs to no organization and deleting it revokes every tenant's grants under it. It remains listable, readable and evaluable"). Its PERMISSION_DENIED prefix is unchanged.

Every cited card (8: 5103, 5190, 5226, 5493, 5973, 6428, 7795, 8158) was read through REST, body and every comment, before its string was rewritten. 5973 answers 404 on both the issue and the pull endpoint; its timeline and its merge commit abeb3751f carry the decision.

The OrphanSweepSubject.issue member

The two sweep reasons above are the values of OrphanSweepSubject.issue, an exported member that sweepOrphanedRowsByRecordExistence appends to its "revoked N rows" warning. The member keeps its name and type (a rename is an export change, outside a text-only stage); only its doc comment changes, from "Issue reference appended to ..." to "Why the rows go, appended to ... Runtime text carries no tracker number, so this states the decision in words", so the published declaration no longer asks a caller for a tracker number.

Translations

None of these strings has a locale variant: they are refusal and log text, not translation keys.

Ledger (scripts/doc-authoring-prose-id.baseline.json)

The ledger is serial across every lane's stages, so this PR opened only after stage 5's PR #21518 merged (e3ad4922). origin/main at ad7c3518 (which also carries stage 4, PR #21472, and another lane's stage, PR #21521) was then merged in (no rebase). The ledger conflicted; main's copy was taken and regenerated on the merged tree with node scripts/check-doc-authoring.mjs --census-ledger (exit 0, no growth refusal). Against main the diff deletes 16 lines and adds none: exactly the four file blocks of this stage. A scripted key-by-key comparison of main's copy against the regenerated one reads 4 keys moved, all of them this stage's, each to absent; every other row is unchanged. No other open PR touches the file.

before (ad7c3518) after
plugin-sharing 7 occurrences, 7 pairs, 3 files 0
plugin-audit (audit-writers.ts) 1 occurrence, 1 pair, 1 file 0
whole ledger 42 occurrences, 35 pairs, 10 files 34, 27, 6 (all service-analytics)

pnpm check:doc-authoring at the head: "sibling-package prose ids hold the baseline — 26 pinned site(s) across 6 file(s), 86200 string(s) read in 1252 parsed source(s), no growth, no burn-down unrecorded". No gate is added or loosened; scripts/check-doc-authoring.mjs is untouched.

Changeset

.changeset/20751-services-strings-stage6-state-the-decision.md: patch for @objectstack/plugin-sharing and @objectstack/plugin-audit. Measured after building the merged tree: the new sentences are in each package's built output (dist/index.js and dist/index.mjs of both). A TypeScript scan of every string literal and template text in the built output finds 0 tracker ids in either package. Control: the same scan reads 34 in service-analytics' built output, the count its ledger entries carry.

Text-only proof

A TypeScript-AST skeleton of each changed .ts file, where every string literal and template text is a placeholder, a run of adjacent string operands of a + chain is one string (only its embedded expressions are kept), identifiers and numbers keep their text, and comments are never read. aa463223 (where the branch was cut) against the head: 6 of 6 SAME, and the same against ad7c3518 (main did not touch any of the six files). Controls on scratch copies of sharing-service.ts, each mutation's marker counted once on disk first: a one-identifier rename reads DIFF; a text-only change reads SAME; a re-split of one string into two concatenated pieces reads SAME.

Pins

  • sharing-service.test.ts:1616: the write-gate failure log line is found by "an abstention would hand the row to the other write authorities" instead of the id; the fail-closed assertion beside it and the deny verdict assertions are unchanged. Reverse check, at the committed 90db7d4e (the merge did not touch these files), through scripts/ablation-replace.mjs under the lock: the new clause put back to the citation form (anchor hit once, blob moved) turned exactly that case red (predicted 1, measured 1 of 131); restored byte-identical to HEAD with an empty git diff HEAD.
  • No other test asserts any of the eight strings by their ids. The two dogfood tests that read the no-active-organization refusal match "active organization", which stays.

Tests

All through scripts/pm/os-verify-lock.sh, every verdict VERDICT command-exit 0, at the head 2f545642 (the merged tree; pnpm install --frozen-lockfile first):

  • Build: turbo run build --filter=./packages/* --filter=./packages/*/* (71/71).
  • @objectstack/plugin-sharing: 38 files, 954 tests passed; @objectstack/plugin-audit: 38 files, 618 tests passed.
  • typecheck for both, including check:test-typecheck: OK for each.
  • The same suites and typecheck were green at 90db7d4e, before the merge.

Gates

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) at 2f545642 (8 paths vs merge base ad7c3518, 62 changed lines): 74 commands, run one at a time from the worktree after the full build, each exit code recorded before any pipe; 74 exit 0. --ran: "74 derived famil(ies) accounted for — 74 run, 0 NOT-MEASURED (a DERIVED zero — all 74 recorded an exit code and none of them is 3)". The same 74 also ran green at 90db7d4e, before the merge.
  • check-issue-citations: "no issue citations added against ad7c351 (5 file(s) read)"; check:i18n: "OK (9 package(s) — all bundles in sync, no undeclared authoring keys)"; check:nul-bytes: OK, 9876 files; check:type-check-debt: "none above its recorded number"; check:dual-build-cjs-loads: 106 require entry points across 66 packages load; check:dts-closure: 71 built packages, 169/169; check:sourcemap-no-sources-content: 68 packages, 532 maps; check:published-files: 69 publishable packages; check:engine-double-contract: OK.
  • Outside the derived set, all exit 0 at 2f545642: the eleven declared wide-population families (check:init-service-contract, check:live-db-isolation, check:meta-type-normalized, check:optional-error-sink, check:resume-authority-declared, check:route-envelope, check:runner-env-posture, check:settings-bind-window, check:startup-registry-verdict, check:verify-stand-in, check:wildcard-fallthrough), plus check:durability-log-level and check:error-code-casing (log and refusal text moved; no level or code did).
  • Lint, narrowed as a measurement: eslint --no-inline-config --format json over the 6 changed .ts files at 2f545642: 6 files linted, 0 errors, 0 warnings. eslint.config.mjs enables no type-aware linting (no parserOptions.project, no typed rules), so this diff cannot move any untouched file's verdict. Repo-wide pnpm lint is CI's.
  • origin/main was still ad7c3518 when this PR opened.

Acceptance notes

Noted, not filed:

  • OrphanSweepSubject.issue now carries a reason in words; its name still says "issue". Renaming it is an export change and belongs to no text-only stage. Carrier: none.
  • plugin-sharing test titles and comments still carry ids ([#6428], #5103, #5190, ...); test bodies and comments are outside the ledger. Carrier: none.

Generated by Claude Code

claude added 2 commits October 2, 2026 22:45
…instead of citing a tracker number

Stage 6 of the services lane's share of the runtime-string burn-down:
every ledgered tracker-number occurrence in plugin-sharing/src and the
one in plugin-audit's audit-writers.ts. Each rewritten string states what
the cited card decided, in words, or drops a citation the sentence
already explained. Text only: no status, error code, field, route or
control flow moves.

The doc-authoring prose-id ledger is recomputed with --census-ledger:
four file entries leave it, nothing else moves.

Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
Co-authored-by: Claude <noreply@anthropic.com>
…stage6

The prose-id ledger conflicted: main's copy was taken and regenerated
with --census-ledger on the merged tree. Against main it deletes 16 lines
and adds none: the plugin-sharing and audit-writers.ts entries go to
zero and no other entry moves.

Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-audit, @objectstack/plugin-sharing, touching 7 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/system-context.mdx (via assertCanDeletePlatformGlobalRule (symbol, a method of class SharingRuleService))
What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 15 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ad7c3518983a1bb63fd4601954ac92d055124e42 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from e80a5f883ed18501af2fc49ce92de414eb79bd45 — the merge of head 2f545642d658baf826ccbd7a0f87b52ef7891822 into base ad7c3518983a1bb63fd4601954ac92d055124e42, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e80a5f883ed18501af2fc49ce92de414eb79bd45 && git checkout e80a5f883ed18501af2fc49ce92de414eb79bd45
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ad7c3518983a1bb63fd4601954ac92d055124e42 2f545642d658baf826ccbd7a0f87b52ef7891822 && git checkout -B drift-repro ad7c3518983a1bb63fd4601954ac92d055124e42 && git merge --no-ff 2f545642d658baf826ccbd7a0f87b52ef7891822

node scripts/docs-audit/affected-docs.mjs --json ad7c3518983a1bb63fd4601954ac92d055124e42

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs ad7c3518983a1bb63fd4601954ac92d055124e42 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 04:02
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 04:02
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 4916168 Oct 3, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20751-services-strings-stage6 branch October 3, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants