Skip to content

feat(spec): IAIConversationService.get / list and IAIService.listPendingActions take the caller's execution context - #23197

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23158-ai-store-caller-context
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23158-ai-store-caller-context

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23158
Clause-②: yes (widening: an optional parameter on three published store-contract methods)

What changes

The three AI store reads take the caller's execution context as an optional trailing parameter. This is the contract change that objectstack-ai/cloud#2844 Q1 ruling C names (maintainer 「同意」, cloud comment 6138290535), in the shape triage's direction gives (6139367236).

Method FROM TO
IAIConversationService.get get(conversationId) get(conversationId, context?: ExecutionContext)
IAIConversationService.list list(options?) list(options?, context?: ExecutionContext)
IAIService.listPendingActions listPendingActions?(filter?) listPendingActions?(filter?, context?: ExecutionContext)

The TSDoc states the rule an implementation follows:

  • Supplied: the implementation passes it unchanged as the context of every engine read the method makes (BaseEngineOptions.context), so the read runs as that caller. Under ADR-0096 D5 strict mode an engine read with no principal is refused with 403.
  • Omitted: the read stays the one the implementation made before this parameter existed.
  • list's options.userId is called out as a filter on the owner. It is not the caller's identity.
  • Whether a caller is admitted to the rows is the stored objects' access and sharing question, which these contracts do not declare.

Files: packages/spec/src/contracts/ai-service.ts (three signatures, their TSDoc, and one type-only import), a pin file beside it, and .changeset/23158-ai-store-caller-context.md (@objectstack/spec minor, carrying the Clause-② line and the FROM → TO). No generated artifact moved (check:generated green, see below). No object's access or sharing changed.

Measurements

H1: "the shape the other store contracts use"

Population at origin/main de24373ec, packages/spec/src/contracts/*.ts (test files excluded): the methods that take the caller's context.

Spelling Contract: methods (lines)
positional context: ExecutionContext, required sharing-service.ts: 15 (:249–:588) · approval-service.ts: 12 (:881–:996) · share-link-service.ts: 3 (:237, :245, :256)
positional context?: ExecutionContext, optional analytics-service.ts: 3 (:312, :329, :351)
positional context?: SecurityContext (a Partial of ExecutionContext) security-service.ts: 20 (:418–:1049)
a field in an options bag data-engine.ts options?: BaseEngineOptions (:289–:311) · objectql-engine.ts:198 · knowledge-service.ts:43 (executionContext?) · automation-service.ts:709

The majority is ExecutionContext from ../kernel/execution-context.zod.js, as a positional parameter after the method's own arguments: 33 methods across 4 contracts. In 31 of the 33 it is the last parameter, and in the other 2 one optional bag follows it. approval-service.ts's listRequests(filter, context) has the same shape as list here. The parameter is optional, as triage directs and as IAnalyticsService already does. The import specifier carries .js, as 5 of the 7 sibling imports do.

Conflict, stated rather than resolved silently: the card body's ask said "the same shape the engine's read verbs take", which is the options-bag form. Triage's direction (Zone 1, binding) says "the shape the other store contracts use", and this PR follows triage. The two forms meet at the engine call: the type-level pin below proves ExecutionContext is assignable to BaseEngineOptions['context'], so an implementation forwards it with no conversion.

H2: where the objects live, and the owner-reads-own-rows reading: NOT MEASURED here

None of the four objects is declared in this repository. On origin/main de24373ec, a git grep for the declaration spelling name: 'OBJECT' returns nothing for ai_conversations, ai_messages, ai_pending_actions or ai_agent_memories. The only hits are test fixtures in plugin-sharing and name references in the share-link routes. The control leg is the same grep for name: 'sys_user', which hits packages/platform-objects/src/identity/sys-user.object.ts:46. This repository's liveness ledger (packages/spec/liveness/agent.json:96) cites the store at cloud @ef5a4344: packages/service-ai/src/agent-memory.ts#AgentMemoryStore — the store, over the object ai_agent_memories (access private, no API). I did not read that file directly.

So triage's measurement ("with a caller threaded through, does an ordinary member read their own rows on these access: private objects?") cannot run in this repository. It can run in objectstack-ai/cloud's service-ai, with this contract at cloud's pin. The cloud card that switches the 18 reads back to the caller's identity is the natural place to measure it. No sharing card is filed on an unmeasured premise. If that measurement shows an owner is refused, the sharing question is filed then, from there.

H3: the pins, without an in-repo implementer

git grep on origin/main finds no implementer of IAIConversationService or of IAIService.listPendingActions in this repository. The only users are ai-service.test.ts's mocks and a type reference in packages/runtime/src/domains/ai.ts:67, which never calls these methods. So the pins in packages/spec/src/contracts/ai-store-caller-context.pin.test.ts are contract-level:

  • Type level (compiled by check:test-typecheck under tsconfig.test.json):
    • each method's trailing parameter is exactly ExecutionContext | undefined, and nothing follows it (arity 1 | 2 / 0 | 1 | 2);
    • ExecutionContext is assignable to the non-null BaseEngineOptions['context'];
    • an implementation written against the shorter signatures still satisfies each method type;
    • calls without the context compile.
  • Runtime: a minimal in-test implementation, over a recording engine port typed as IDataEngine's find / findOne (a Pick), is written to the TSDoc's forwarding rule.
    • With a member context, every engine read (get makes two, ai_conversations then ai_messages) carries it as options.context, by reference, and the real BaseEngineOptionsSchema.safeParse accepts those options.
    • Without one, each engine read gets no options argument at all, which is the call the shorter signature made.
    • The runtime half pins the rule as executable reference. It cannot pin service-ai, and that implementer's own conformance lives with it.

Reverse verification (ablation), one per method, from the committed state (the HEAD blob of ai-service.ts is 84b68834da23). Each leg ran through node scripts/ablation-replace.mjs: the anchor hit 1 → 0, the blob changed, and on restore the blob equalled HEAD and git diff HEAD was empty. No dist/ rebuild was needed, because the pin imports ./ai-service and ../data/data-engine.zod by relative path, so the program reads src/. Measured with the spec test-layer program (tsc --noEmit -p tsconfig.test.json):

Leg Pin-file errors Program total What went red
baseline (HEAD) 0 246 (the ledger's 246) none
get without context 7 253 GetTakesTheCallerContext and GetArity (TS2344), TS2493, the in-test get (TS2322 / TS7006), the two-argument call (TS2554)
list without context 7 253 ListTakesTheCallerContext and ListArity, the same classes
listPendingActions without context 7 253 ListPendingActionsTakesTheCallerContext and ListPendingActionsArity, the same classes

The observed direction is turning red, at the type layer. The runtime half stays green under each ablation, because types are erased at run time. That is expected: the contract is a type, and the type half is what fails.

H4: the typecheck debt ledger

The pinned entry src/contracts/ai-service.test.ts: TS2739 … missing … getTurnState, update is unmoved. A mock written against the shorter get / list stays assignable, so no new signature arrives. check:test-typecheck is green with the ledger untouched: 52 file(s) / 246 error(s) / 135 pinned signature(s) held. The new pin file compiles with zero errors. packages/spec/test-typecheck-debt.json is not in this diff.

Verification

Readings taken at HEAD c128fd10a: this branch after merging origin/main 32fa1942e, which touched packages/spec in disjoint files.

  • pnpm --filter @objectstack/spec build: VERDICT command-exit 0. The rebuilt dist/contracts/index.d.ts carries the three new signatures (:5745, :6084, :6094).
  • pnpm --filter @objectstack/spec check:generated: exit 0, All 14 generated artifacts are up to date. That includes check:api-surface and check:export-origins (no export added or removed) and check:test-typecheck.
  • pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/contracts/ai-store-caller-context.pin.test.ts src/contracts/ai-service.test.ts: Test Files 2 passed (2), Tests 27 passed (27).
  • The full spec suite, pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: Test Files 662 passed (662), Tests 20114 passed | 1 todo.
  • pnpm --filter @objectstack/spec typecheck: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 85 commands. Each line was run with its exit code captured before any pipe.
    • 82 exited 0 on the first pass.
    • 2 exited 3 (PREREQUISITE NOT MET) and went green after their prerequisites were built. check:doc-formula-expressions passed after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint: 22 record-scoped formula example(s) across 474 files / 1391 TS blocks judged clean. check:lean-entry-closure passed after building @objectstack/objectql: Admitted set held exactly (15 packages).
    • 1 is NOT MEASURED: check:dual-build-cjs-loads, which needs every package's dist/, so a whole-workspace build. As a declared narrowing, all 19 @objectstack/spec CJS require entries the gate lists (--list) load in a fresh node with 0 failures. The diff adds only a type-only import, which is erased from the emitted JS.
    • --ran reconciliation: 85 derived famil(ies) accounted for — 84 run, 1 NOT-MEASURED.
  • ESLint, narrowed and proven: pnpm exec eslint --no-inline-config --format json over the two changed .ts files reports 2 files, 0 errors and 0 warnings. Both files are in the config's population (--print-config resolves rules for each). The config enables no type-aware linting (eslint.config.mjs:327–:328: no parserOptions.project), so this diff cannot move the verdict on any untouched file. The full pnpm lint is CI's run.
  • Consumers: none in this repository call the three methods (H3). The public surface widens by an optional parameter only, so no import-side test is owed here.

Acceptance notes

  • The card body's asks 2 and 3 are not in this PR. Ask 2 (owner reads of the four objects) and ask 3 (a non-owner refused, the owner admitted) concern the objects, which are declared in objectstack-ai/cloud. Triage's direction keeps sharing out of this card ("⛔ no sharing widened as a rider") and routes it to a card filed only after the measurement in H2. That measurement can only run where the objects are declared.
  • Other contract reads with no caller context, noted and not filed. IAIConversationService.getTurnState reads the turn's ai_messages, and addMessage / update answer with the conversation. None of them takes a caller context. The ruling and triage name exactly three methods, so this PR changes those three. Carrier: the cloud card that switches the 18 reads back, which will see whether any of these is among the reads D5 refuses.

Generated by Claude Code

…cution context

IAIConversationService.get / list and IAIService.listPendingActions gain an
optional trailing `context?: ExecutionContext`, the type and position the
other store contracts use. An implementation forwards it as the engine
read's `options.context`; an omitted context leaves the read unchanged.
Type-level and runtime pins beside the contract.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s).

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

  • content/docs/kernel/contracts/index.mdx (via IAIService (symbol, a top-level interface))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via IAIConversationService (symbol, a top-level interface), IAIService (symbol, a top-level interface))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 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 — 141 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 8323cad97e148fb1955511479adacad19aee10fd → packageMentionDocs.

Which tree this was computed on

This run read content/docs from cb636e7e7125ae612bb81e055672783713eb4edd — the merge of head c128fd10a116ff96c85f750dc744c70bf7008c6f into base 8323cad97e148fb1955511479adacad19aee10fd, 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 cb636e7e7125ae612bb81e055672783713eb4edd && git checkout cb636e7e7125ae612bb81e055672783713eb4edd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8323cad97e148fb1955511479adacad19aee10fd c128fd10a116ff96c85f750dc744c70bf7008c6f && git checkout -B drift-repro 8323cad97e148fb1955511479adacad19aee10fd && git merge --no-ff c128fd10a116ff96c85f750dc744c70bf7008c6f

node scripts/docs-audit/affected-docs.mjs --json 8323cad97e148fb1955511479adacad19aee10fd

⚠️ 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 8323cad97e148fb1955511479adacad19aee10fd → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c128fd10a116ff96c85f750dc744c70bf7008c6f
Local-runs: none

Inputs read, and nothing else: card #23158 (body and all 4 comments: triage 6139367236, claim 6139494839, os-dev-report 6140762386, seat ACCEPT 6140802170); PR #23197 body, file list (3 files, +292 / −15) and the net diff against main; the head's check-runs, read twice (43 runs each time); code at origin/main 8323cad97 and at the head via git show / git grep only. The seat's ACCEPT was treated as a claim to test, not as evidence. The head is 8aaa06435 merged with origin/main 32fa1942e; main has moved 5 commits since, none touching packages/spec/src/contracts/, kernel/execution-context.zod.ts, data/data-engine.zod.ts, .changeset/pre.json or the typecheck debt ledger (empty diff-stat over those paths), so the judgments below hold against current main too.

① Derived judgments

  1. Public surface — three interface methods gain an optional trailing parameter. Right. On main (ai-service.ts :447, :775, :782) listPendingActions?(filter?), get(conversationId) and list(options?) carry no context; the head adds context?: ExecutionContext as the last parameter of each and one type-only import of ExecutionContext from ../kernel/execution-context.zod.js. No export is added, removed or renamed, and no object, Zod schema, or authorable key moves. The changeset's FROM → TO rows are true to the diff.
  2. Accept set at the type layer — widened, nothing narrowed. Right. An implementation with fewer parameters remains assignable to each method type (the pin's shorterGet / shorterList / shorterListPendingActions), and a call that omits the argument still compiles (the pin's "omitted" cases are written that way). The only in-repo implementer-shaped code is the mock in ai-service.test.ts :287 (shorter get / list), whose debt-ledger entry (TS2739 … getTurnState, update) is the one it carried before — no new signature arrives, so the ledger stays unmoved. packages/runtime/src/domains/ai.ts:67 only types a variable and calls none of the three. No implementer of IAIConversationService or IAIService.listPendingActions exists in this repository; the measured consumer is cloud's service-ai, which is still assignable for the same reason.
  3. Type and position — the sibling store contracts' shape. Right. Counted on origin/main in packages/spec/src/contracts/*.ts (tests excluded): sharing-service.ts 15, approval-service.ts 12 and share-link-service.ts 3 take a positional context: ExecutionContext after the method's own arguments (required); analytics-service.ts 3 take the same, optional (query(query, context?) :312, generateSql?(…, context?) :329, queryDataset? :351). approval-service.ts listRequests(filter, context) :850 is the same shape as list(options?, context?) here. The PR's changeset says "the type and the position", not the optionality — precise, since three of the four siblings make it required and triage asked for optional. The .js specifier matches 5 of the 7 sibling imports; the two extensionless ones (automation-service.ts, knowledge-service.ts) are the minority.
  4. The card asked for an options bag ("the same shape the engine's read verbs take"); triage directed the positional form. The PR follows triage and declares the conflict. Right. The two forms meet at the engine call: BaseEngineOptions.context is ExecutionContextSchema.partial().optional() (data-engine.zod.ts:75) and ExecutionContext is z.input of the same schema (execution-context.zod.ts:518), so the contract's type is assignable to the engine option with no conversion — pinned by ForwardsToTheEngineRead.
  5. TSDoc / changeset sentence — "Under ADR-0096 D5 strict mode an engine read with no principal is refused with 403, so without it an implementation can only read as system." TRUE. security-plugin.ts:377 isPrincipalLessContext (no userId, no positions, no permissions), :392 principalLessDenial builds PermissionDeniedError (403 PERMISSION_DENIED), thrown at :8730 inside resolveOperationPrincipals, which the engine middleware registered at :2319 reaches (:2568) for every verb after the isSystem bypass at :2365; getReadFilter :5614 answers the deny filter for the same predicate. A read with no options at all is principal-less by that predicate, so it is refused too. ADR-0096 D5 and its 2026-10-08 note record strict mode ON with no OFF switch, so "under strict mode" is the only mode there is. The two admitted forms are the caller's principal or isSystem: true — "can only read as system" is exact.
  6. "passes it unchanged as the context of every engine read the method makes." A contract obligation, stated as one. Right. It is normative text for implementers, not a description of code in this repository (there is none). It is consistent with IDataEngine.find / findOne (objectName, query?, options?: BaseEngineOptions) (contracts/data-engine.ts:289, :306) and with judgment 4. It binds cloud's service-ai, which is not measured here; the pin's header says so.
  7. The userId sentence — "options.userId stays a filter on the conversation's owner. It is not the caller's identity." TRUE. AIConversation.userId is documented "User who owns the conversation" (:711), and list's userId filters on it (the existing mock filters c.userId === options.userId). Nothing in the contract reads it as a principal.
  8. The access/sharing sentence — "Whether an ordinary member is admitted to their own rows depends on the stored objects' access and sharing, which these contracts do not declare." TRUE. The contract names no object declaration. git grep on origin/main for name: 'ai_conversations' hits only plugin-sharing test fixtures and a runtime test; ai_messages two test fixtures; ai_pending_actions and ai_agent_memories zero; the control name: 'sys_user' hits 89 files including platform-objects/src/identity/sys-user.object.ts. The liveness ledger (packages/spec/liveness/agent.json, memory row) places AgentMemoryStore and ai_agent_memories in cloud.
  9. Pins — what they prove. Right, within their stated limit. Type level: Eq on Parameters[1] equals ExecutionContext | undefined for each method; arity tuples 1 | 2 and 0 | 1 | 2 prove nothing follows; assignability to the engine option; shorter implementations assigned to the method types. The file is under src/**/*, so tsconfig.test.json compiles it (moduleResolution: bundler, which is why its extensionless relative imports are fine there, as in data-engine.test.ts and objectql-engine.test.ts), and vitest's local project includes src/**/*.test.ts. Runtime: a reference implementation over a recording port typed Pick of IDataEngine shows the supplied context arrive as options.context by reference on every read (get makes two), BaseEngineOptionsSchema.safeParse accepting it, and an omitted context producing a read with no options argument. That half is executable reference for the rule, not a proof about any shipped implementation — the correct limit when no implementer lives here. The dev's ablation reading (0 → 7 pin-file errors per method, type layer) was not re-run; the Assert on Eq is the shape that reds on reversion, and the verdict here does not rest on it.
  10. Generated artifacts — none owed. Right. api-surface/contracts.json records IAIConversationService (interface) by existence only; api-surface-signatures.json hashes only the define* functions; export-origins maps names to files. A method-signature change moves none of them, which matches the diff carrying none.
  11. Scope held to the three named methods. Right. Inside IAIService, approvePendingAction?(id, actorId) and rejectPendingAction?(id, actorId, reason?) carry an attribution actorId, not an execution context; getTurnState, addMessage and update on the conversation service carry none. All are outside the ruling and triage's three; see ③.

② Semver level

@objectstack/spec: minor, Clause-②: yes (widening: an optional parameter on three published store-contract methods) on the PR body's line 2 and in the changeset body. Right.

  • The diff widens a published type surface (three optional parameters) and narrows nothing, so yes (widening) is the true declaration; widening is read as non-breaking by check-adr-0087-registration.mjs (signal 4 fires on narrowing only), so no ADR-0087 disposition marker is owed, and none is present. The arm parses: clause2-line.mjs reads the first token inside the parenthesis and (widening: is admitted (a colon is neither an identifier character nor a pipe).
  • Pre mode: .changeset/pre.json is {"mode": "pre", "tag": "next"} on both main and the head, with pending major changesets for @objectstack/spec already in stock (11509-…, the 19939-… family) and PROTOCOL_VERSION 18.0.0 one ahead of the package's 17.7.0 — the lockstep shape check-changeset-no-major.mjs admits only in pre mode. Pre mode stands down the launch-window guard on major; it does not change the LEVEL axis, which requires a Clause-②: yes PR to grade at least one moved published package minor or above. minor satisfies it, and in the fixed group under a pending major the bump is absorbed into the next -next prerelease, so minor is the right level and the lowest true one. The Check Changeset run on the head concluded success.

③ Boundary flags

Every dev flag (deviations, out_of_scope_findings; open_questions is empty) and the brief's questions, answered or escalated:

  • Deviation 1 — shape (options bag vs positional). Answered: triage's direction is the binding one and the PR says so rather than resolving it silently; judgment 4 shows the two forms meet at the engine call. No action.
  • Deviation 2 — merged origin/main 32fa1942e before verification. Answered: the merge is in the head, disjoint from the contract; main has moved 5 commits since, none touching the PR's inputs (header above). The queue rebuilds against current main.
  • Deviation 3 — the verify lock held 1135 s. Recorded as fact; no bearing on the contract.
  • Deviation 4 — attribution trailers. Answered: commit 8aaa06435 carries Claude-Session: and the model-free Co-authored-by pair AGENTS.md prescribes, and the PR body the session-URL footer. Consistent with the rule that governs this repository.
  • Deviation 5 — file surface held. Confirmed from the file list: the contract, one pin file beside it, one changeset; no generated artifact, no debt-ledger edit, no access or sharing change, no implementation outside spec, no governed surface.
  • Out-of-scope 1 — getTurnState, addMessage, update carry no caller context. Answered, with a condition: the ruling and triage name exactly three methods, so leaving these three out is right for this PR. The card body's census counts 9 refused reads in cloud's conversation store, which is more than get and list can account for, so the cloud card that switches the 18 reads back is the right carrier and must say which methods its reads go through; if any goes through one of these three, a follow-up spec card is owed then, with that reading as its evidence. Not a FAIL condition here.
  • Out-of-scope 2 and the brief's H2 question — owner-reads-own-rows NOT MEASURED; card asks 2 and 3 not delivered. Disposition right: the four objects are declared in cloud (judgment 8), so the measurement cannot run in this repository, and triage ("⛔ no sharing widened as a rider"; the sharing card is filed only on a measurement) forbids filing on an unmeasured premise. The pin's header states the limit. Escalated to the owning seat's landing record: because the PR closes the card with Fixes #23158 while asks 2 and 3 remain open, the landing record on spec(ai): the AI store contracts take the caller's execution context — IAIConversationService.get/list and IAIService.listPendingActions carry none, so a D5-strict implementation can only read as system #23158 must name the cloud card as their carrier so the obligation survives the closure.
  • Check-runs on the head, as read (second read). Success: Lint & Repo Gates, every Lint · sub-job, Build Core, Governed Surface Queue Guard, Check Changeset (both runs; one was in progress at the first read), Type Check · debt ledger, Type Check · source gates, Check PR Size, Spec property liveness, Dogfood Regression Gate, Flag docs affected by code changes, Check Documentation Links, the three claim/closing-target guards, Auto Label, filter. In progress: Test Core 1–6 of 6, Type Check · workspace, Type Check · consumer gates. Skipped: Build Docs, Console Pin Gate, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Packed-tarball smoke (opt-in), the sharded dogfood matrix entry, one Auto Label and one Check PR Size duplicate. No run has failed. The required TypeScript Type Check and Test Core contexts had not concluded at read, so the PR is not yet green for the queue; this verdict is on the diff, and the landing still waits for every check green.
  • PR shape for the landing. Draft, same-repo head, 307 changed lines, label needs:contract-review present, no governed path in the file list. Nothing here reserves the landing beyond this record and green checks.

Implemented-by: claude/issue-23158-ai-store-caller-context
Reviewed-by: session_012uFegdfR6K6uqvLzt7XsdZ

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 13, 2026 08:12
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 107853c Oct 13, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23158-ai-store-caller-context branch October 13, 2026 08:42
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/m tests tooling

Projects

None yet

2 participants