Ars Umbris scope evidence

Read-only local object inspection for the comparison. No checkout materialization, acquisition, dependency installation or target execution. Full pins resolve entry VERSION.md abbreviations; branch HEADs were not used as evidence pins. Extract line numbers refer to committed blobs. SHA-256 values cover whole blob bytes, not rendered extracts.

Available revisions

Repository Full commit Local release tag equals pin
arsumbris a9f6cceedb191b8504443222d55d3106f9f36ba9 yes
au-engine 46f12389d4d4833fed51640a48cf938516433106 yes
au-engine-sdk ffeca7a745af6f8069d5d2151fc96bae964825dd yes
au-mcp 3ddc3b62ed0b339a89a57f11026676df18d3b2eb yes
au-mcp-sdk 91209bb00cebde36e6454207a89e257f20573577 yes
au-mcp-core 25e0625e553c237671bba9a3a8c173a889dddf02 yes
au-mcp-adapter-cc 333b2117f338cd4815eeeb9ab7b3932b29fa7d62 yes
au-mcp-adapter-codex 3b0e3a3cf67b3143819c61c590efc85d36c23bc5 yes
au-defaults 0879c5ef432b468b9fa0e4a3e52d2fb58d2ec504 yes
au-type-system 066fd06b224bc46358273ebfbc82b6b8863e9885 yes
au-host 0e85fb1731fdef2d6196618aff5a2a567e621866 yes
au-type-codegen e3c63da4afc77c2979ea06763c8c1045bcd93644 yes

Discriminating extracts

arsumbris: VERSION.md, lines 1–33

Whole-blob SHA-256: 24dfb38dd523ba19b5f271e4ef61c141cc24361c0bd583346bc02c7ace6adaca. Revision: a9f6cceedb191b8504443222d55d3106f9f36ba9.

1: <!-- generated by scripts/release.py at release time -->
2: <!-- do not edit by hand -->
3:
4: # arsumbris 0.0.1-alpha
5:
6: The parts this release ships. Each `commit` is that part's public repository commit tagged `0.0.1-alpha`.
7:
8: | part | commit | internal version |
9: | --- | --- | --- |
10: | arsumbris | — | — |
11: | au-engine | 46f1238 | wire schema 29 |
12: | au-engine-sdk | ffeca7a | — |
13: | au-type-system | 066fd06 | — |
14: | au-host | 0e85fb1 | mount contract 7 |
15: | au-mcp | 3ddc3b6 | mcp wire 0 |
16: | au-mcp-sdk | 91209bb | — |
17: | au-mcp-core | 25e0625 | — |
18: | au-mcp-adapter-cc | 333b211 | — |
19: | au-mcp-adapter-codex | 3b0e3a3 | — |
20: | au-type-codegen | e3c63da | — |
21: | au-defaults | 0879c5e | — |
22: | au-base-types | 0a0c977 | — |
23: | au-weave | 2432073 | — |
24: | au-agent-guides | ddc3333 | — |
25: | au-rules | 4be3274 | — |
26: | au-writing-style | 163b559 | — |
27: | au-skills | d32336d | — |
28: | au-govern | 8bf899b | — |
29: | au-competency | 6f23713 | — |
30: | au-ingest | 5b3c22f | — |
31: | au-tree-research | 65faf72 | — |

au-defaults: mcp-bundle/.arsumbris/repo.yaml, lines 1–11

Whole-blob SHA-256: 7ffc741c782650c72b947b370f33eebed91d6c57e6548a5acec59d80e8a7abc1. Revision: 0879c5ef432b468b9fa0e4a3e52d2fb58d2ec504.

1: # The agent/mcp framework bundle
2: # A pure AGGREGATOR node (au.engine.repo).
3: type: au.engine.repo::au-engine
4: name: mcp-bundle
5: description: aggregator for the agent/mcp framework type-def owners
6: deps:
7:   - name: au-mcp
8:   - name: au-mcp-sdk
9:   - name: au-mcp-core
10:   - name: au-mcp-adapter-cc
11:   - name: au-mcp-adapter-codex

au-defaults: studio/profiles/Claude.yaml, lines 1–2

Whole-blob SHA-256: 82722d3c85581ee9de20123b743705f4869aa618face5b2e6a75c5412e63849a. Revision: 0879c5ef432b468b9fa0e4a3e52d2fb58d2ec504.

1: type: agent-profile::au-mcp-sdk
2: adapter: "[[mcp.adapter.cc::au-mcp-adapter-cc]]"

au-mcp: package.json, lines 20–27

Whole-blob SHA-256: a4e50ea04bbbd7db4ace57e0eef7c8d39fc2964bd84602eaf792a0821efe7fab. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

20:     "@arsumbris/type-codegen": "link:../au-type-codegen",
21:     "@arsumbris/au-mcp-sdk": "link:../au-mcp-sdk"
22:   },
23:   "devDependencies": {
24:     "@types/node": "^25.9.1",
25:     "typescript": "^6.0.3",
26:     "vitest": "^4.1.7"
27:   }

au-mcp: src/daemon/discovery.ts, lines 15–23

Whole-blob SHA-256: 29ea7497ebdd733df7250345aed63cf188c7efef1b97539aa26a88699f7a39d1. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

15: import * as path from 'node:path'
16: import { pathToFileURL } from 'node:url'
17:
18: import { buildIR, emitJsonSchema } from '@arsumbris/type-codegen'
19: import type { WireTypeDef } from '@arsumbris/type-codegen'
20:
21: import type { Plugin, PluginContext, PluginManifest, PluginModule, PluginShape, PluginTier } from '@arsumbris/au-mcp-sdk'
22: import { PLUGIN_CONTRACT_VERSION } from '@arsumbris/au-mcp-sdk'
23:

au-mcp: src/daemon/discovery.ts, lines 318–339

Whole-blob SHA-256: 29ea7497ebdd733df7250345aed63cf188c7efef1b97539aa26a88699f7a39d1. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

318:     if (!def.source?.file) continue
319:     const derived = manifestFrom(def.name, metaRecord(block))
320:     if (!derived) continue
321:     // Enforce the kernel<->plugin handshake: exact equality against the kernel-expected
322:     // PLUGIN_CONTRACT_VERSION (loud fail-closed, no override — decision 2609091649). A skewed
323:     // plugin is REFUSED, never loaded at the wrong contract. Routed through the existing
324:     // critical/non-critical ethos: a `critical` plugin fails the daemon closed (refuse to
325:     // serve, like a critical load failure); a non-critical one is skipped with a loud warning.
326:     if (derived.manifest.contractVersion !== PLUGIN_CONTRACT_VERSION) {
327:       const msg = `plugin contract v${derived.manifest.contractVersion} != kernel-expected v${PLUGIN_CONTRACT_VERSION}`
328:       if (derived.manifest.critical) failedCritical.push({ id: derived.manifest.id, error: msg })
329:       else process.stderr.write(`au-mcp: skipping ${derived.manifest.id}: ${msg}\n`)
330:       continue
331:     }
332:     // Provenance: a loadable tool is CONTRIBUTED by the package that owns its def (the
333:     // subtype's owner repo).
334:     derived.manifest.provenance = def.repo ?? derived.manifest.provenance
335:     // Access: the SAME `tool-access-meta` level the tool declares for its own scoped broker
336:     // (below) also rides its manifest, so a mediator classifies it at decide. `requestedBroker`
337:     // reads the def's meta. TOOLS only — a hook declares no tool-access-meta and gets a
338:     // read-only broker from the daemon.
339:     if (derived.manifest.kind === 'tool') derived.manifest.access = requestedBroker(def)

au-mcp: src/daemon/daemon.ts, lines 754–775

Whole-blob SHA-256: 08e0d51517af8a4b3f4047cde65608ffd1ef180956f22b52d50ace2faf2b0384. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

754:       case 'mediate': {
755:         const session = requireSession(request.session)
756:         // The mediate phase: mediators decide in priority order, first non-allow wins.
757:         // A mediator may emit() events (e.g. redirect's tool_denied), which the daemon
758:         // appends to the log + fans to observers — the kernel sequences decide -> record,
759:         // so deny and trace cannot desync. The context (the emit/approval/broker/accessOf
760:         // handles) is shared with the post-tool `review` turn via `mediationContextFor`.
761:         const ctx = mediationContextFor(session)
762:         // CONTINUITY Tier-2 (required capability lost): fail the session closed on WRITES. A
763:         // state-touching action (a mutator) is denied with the recovery notice; reads + non-write
764:         // escapes pass. The runtime sibling of the load-time `poisoned` refusal, scoped to this
765:         // session + to mutators.
766:         if (continuityWriteBlocked(session) && ctx.accessOf(request.action) === 'read-write') {
767:           return { kind: 'decision', id: request.id, decision: { kind: 'deny', reason: continuityBlockNotice(session) } }
768:         }
769:         // ALL mediators are now DISCOVERED plugins from au-mcp-core (plan 2608261532, Phase 5):
770:         // the redirect (mcp.nativeToolRedirect @100), read-before-write (mcp.read-guard @40), and
771:         // tool-precondition (mcp.tool-precondition @30). They ride `registry.mediators()`,
772:         // priority-sorted in `runMediators`, and read per-session state via `MediationContext`
773:         // (`nativeToolAllowlist`, `readHash`/`hasRead`). The kernel wires no floor literal.
774:         const decision = await runMediators(registry.mediators(), request.action, ctx)
775:         return { kind: 'decision', id: request.id, decision }

au-mcp: src/daemon/daemon.ts, lines 885–909

Whole-blob SHA-256: 08e0d51517af8a4b3f4047cde65608ffd1ef180956f22b52d50ace2faf2b0384. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

885:         // Gate-side validation: refuse a malformed input against mcp.tool.<tool> before
886:         // acting. Scope to the tool's OWNER repo (its manifest `provenance`): every tool's def
887:         // lives in the package that shipped it (au-mcp owns none).
888:         const invalid = await checkToolInput(broker, request.tool, request.input, plugin.manifest.provenance)
889:         if (invalid) return { kind: 'invoked', id: request.id, result: invalid, isError: true }
890:         // STAMP ATTACH (the stamper shape): on a governed write, sanitize the input then run the
891:         // registered stampers, injecting their stamp un-forgeably (overwriting any agent-supplied
892:         // one), AFTER gate-side validation so the stamp is daemon-internal, not agent schema. The
893:         // strip is unconditional for a stampable write (a session-less one gets no stamp but is
894:         // still sanitized); a non-write / non-stampable tool is untouched (returns undefined).
895:         let effInput = request.input
896:         const injected = await stampInputForWrite(request.tool, request.input, registry.stampers(), {
897:           session: invSessionId,
898:           launch: invInfo ? launchOf(invInfo, invSession?.nativeToolAllowlist) : undefined,
899:           now: () => new Date().toISOString(),
900:           hookConfigs: invSession?.hookConfigs,
901:         })
902:         if (injected !== undefined) effInput = injected
903:         const result = await plugin.invoke(effInput, {
904:           session: invSessionId,
905:           launch: invInfo ? launchOf(invInfo, invSession?.nativeToolAllowlist) : undefined,
906:           // A read-only trace handle when the call carries a session, so a status/reporting
907:           // callable computes its own payload instead of leaning on a mediator inject.
908:           consultTrace: invSession ? makeConsultTrace(invSession) : undefined,
909:         })

au-mcp: src/daemon/broker.ts, lines 238–271

Whole-blob SHA-256: 06c4d0ee50d095cc86e8451e5a4e712ef85bf64da99e47e14706cbdcaa9d6d60. Revision: 3ddc3b62ed0b339a89a57f11026676df18d3b2eb.

238: function runMutation(client: DaemonClient, verb: string, args: Record<string, unknown>): Promise<TypedMutate> {
239:   const path = String(args.path ?? '')
240:   const expectedHash = typeof args.expected_hash === 'string' ? args.expected_hash : undefined
241:   // The daemon-injected stamps LIST (the stamper shape), when this write carries any. The mcp-sdk
242:   // Stamps a stamper produced ride here as engine-sdk Stamps (the mirror-pin above proves the element
243:   // shapes match); all fold into the write's ONE commit (engine schema 24's `stamps` rider).
244:   const stamps = (args.stamps ?? undefined) as Stamp[] | undefined
245:   // The daemon-injected `ensure_mixins` rider (engine schema 25), the type-claim sibling of `stamps`.
246:   // Carried on the same three verbs the SDK accepts it on (write_file / edit_file / rename); mapped
247:   // to the engine-sdk camelCase options. The optional strict flag is omitted unless explicit, so the
248:   // engine default (strict) stands.
249:   const ensureMixins = Array.isArray(args.ensure_mixins) && args.ensure_mixins.length > 0
250:     ? (args.ensure_mixins as string[])
251:     : undefined
252:   const mixinOpts =
253:     ensureMixins !== undefined
254:       ? {
255:           ensureMixins,
256:           ...(typeof args.ensure_mixins_strict === 'boolean' ? { ensureMixinsStrict: args.ensure_mixins_strict } : {}),
257:         }
258:       : {}
259:   // The daemon-injected `attribution` rider (engine schema 26), the commit-trailer sibling of `stamps`.
260:   // Carried on write_file / edit_file / delete_file (the verbs the engine attribution rider accepts);
261:   // mapped to the engine-sdk camelCase option. Absent/empty -> no option (the engine writes no trailer).
262:   const attribution =
263:     Array.isArray(args.attribution) && args.attribution.length > 0 ? (args.attribution as AttributionEntry[]) : undefined
264:   switch (verb) {
265:     case 'write_file':
266:       return client.writeFile(path, String(args.content ?? ''), {
267:         ...(expectedHash !== undefined ? { expectedHash } : {}),
268:         ...(stamps ? { stamps } : {}),
269:         ...mixinOpts,
270:         ...(attribution ? { attribution } : {}),
271:       })

au-mcp-core: src/read-guard.ts, lines 77–97

Whole-blob SHA-256: 8592c14a84b8f48010ce0b7d25c8e6b8b57bebdead28fb69cb717ee48e8ff7bf. Revision: 25e0625e553c237671bba9a3a8c173a889dddf02.

77:   return async (action: PendingAction, ctx: MediationContext): Promise<Decision> => {
78:     const gatePrefix = ctx.launch.gatePrefix
79:     if (!gatePrefix || !action.tool.startsWith(gatePrefix)) return { kind: 'allow' }
80:     const base = action.tool.slice(gatePrefix.length)
81:     if (!OVERWRITE_TOOLS.has(base)) return { kind: 'allow' } // reads, edit_file (self-guards), etc.
82:     const path = filePathOf(action.input)
83:     if (!path) return { kind: 'allow' } // malformed; gate-side validation handles it
84:     // An explicit expected_hash means the caller is doing its own CAS — let it through.
85:     if (action.input && typeof action.input === 'object' && 'expected_hash' in action.input) return { kind: 'allow' }
86:     // A CREATE (target does not exist) has nothing to clobber, so it needs no prior read.
87:     if (!exists(path)) return { kind: 'allow' }
88:     const seen = ctx.readHash(path)
89:     if (seen === undefined) {
90:       return deny(ctx, action, `read ${path} through the gate before overwriting it (read-before-write)`)
91:     }
92:     const now = await currentContentHash(ctx.broker, path)
93:     if (now !== null && now !== seen) {
94:       return deny(ctx, action, `${path} changed on disk since you read it — re-read it before overwriting (read-before-write)`)
95:     }
96:     return { kind: 'allow' }
97:   }

au-mcp-core: src/file-tools.ts, lines 243–261

Whole-blob SHA-256: e007df645a757ac59a3dcb2ecadbe20ff036f5ac9888ac1941acf30d4c1204d8. Revision: 25e0625e553c237671bba9a3a8c173a889dddf02.

243:     const content = optString(input, 'content')
244:     if (!file_path) return fail("'file_path' is required")
245:     if (content === undefined) return fail("'content' is required")
246:     if (!isAbsolute(file_path)) return fail(`file_path must be absolute, got: ${file_path}`)
247:     const guarded = guardWritePath(workspace, file_path)
248:     if (guarded) return fail(guarded)
249:     if (!broker || !broker.available()) return noEngine('write')
250:     // expected_hash: explicit from the caller, else the read-guard mediator injects it
251:     // (read-before-write). Absent -> the engine overwrites regardless (a new file).
252:     const expected_hash = optString(input, 'expected_hash')
253:     const args: Record<string, unknown> = { path: file_path, content }
254:     if (expected_hash !== undefined) args.expected_hash = expected_hash
255:     const stamps = optStamps(input)
256:     if (stamps) args.stamps = stamps
257:     forwardMixins(input, args)
258:     const attribution = optAttribution(input)
259:     if (attribution) args.attribution = attribution
260:     try {
261:       return mutationResult(workspace, await broker.mutate('write_file', args), file_path, 'wrote')

au-mcp-core: type/mcp.hook.read-guard.type.yaml, lines 20–27

Whole-blob SHA-256: 5d03ffc073d6a3719332213c2495b23cc4b057cf9c173460e8b45a236be27e8c. Revision: 25e0625e553c237671bba9a3a8c173a889dddf02.

20: extends: mcp.hook::au-mcp-sdk
21: meta:
22:   - type: plugin-runtime-meta::au-mcp-sdk
23:     entry: ./src/read-guard.ts
24:     contractVersion: 0
25:     shapes: [mediator]
26:     tier: floor
27:     critical: true

au-mcp-core: type/mcp.tool.write_file.type.yaml, lines 1–14

Whole-blob SHA-256: 628bc974141076db8bc53388ce2af2d83d2ec123155780ecc657b52f4270a68d. Revision: 25e0625e553c237671bba9a3a8c173a889dddf02.

1: extends: mcp.tool::au-mcp-sdk
2: fields:
3:   #: absolute path to write; parent dirs are created.
4:   file_path: String
5:   #: the full file contents (overwrites any existing file).
6:   content: String
7: meta:
8:   - type: plugin-runtime-meta::au-mcp-sdk
9:     entry: ./src/tool-write-file.ts
10:     contractVersion: 0
11:   - type: tool-access-meta::au-mcp-sdk
12:     broker: read-write
13:   - type: tool-presentation-meta::au-mcp-sdk
14:     description: "Write a file, overwriting if it exists. Absolute path; parent dirs created. Prefer edit_file for partial changes. Writes require an editable member (a consumed dep/discover or read-only cache snapshot rejects)."

au-mcp-adapter-cc: hooks/pre-tool-use.ts, lines 8–24

Whole-blob SHA-256: 3f865c4850536841f6f809448508743bf6ff0d72a9f0842497d4c62747133529. Revision: 333b2117f338cd4815eeeb9ab7b3932b29fa7d62.

8: const payload = await readPayload(process.stdin)
9: const tool = String(payload.tool_name ?? '')
10: const input = payload.tool_input ?? {}
11:
12: const decision = await mediateAction(payload, { tool, input })
13:
14: if (decision.kind === 'deny') {
15:   process.stdout.write(
16:     JSON.stringify({
17:       hookSpecificOutput: {
18:         hookEventName: 'PreToolUse',
19:         permissionDecision: 'deny',
20:         permissionDecisionReason: decision.reason,
21:       },
22:     }),
23:   )
24:   process.exit(0)

au-mcp-adapter-cc: src/bridge.ts, lines 120–151

Whole-blob SHA-256: 5b097ee946e3b9a5a7bf90a8ae7b5a86684448b287a963c58fae01f2d4eba315. Revision: 333b2117f338cd4815eeeb9ab7b3932b29fa7d62.

120: /**
121:  * Open a daemon connection for the payload's workspace, ensure the session
122:  * exists (idempotent session-open declaring CC's surface), run `fn`, then close.
123:  * Returns `fallback` if the daemon is unreachable or anything throws — capture
124:  * must never break the hook.
125:  */
126: async function withSession<T>(
127:   payload: Record<string, unknown> | null,
128:   fn: (client: DaemonClient, session: string) => Promise<T>,
129:   fallback: T,
130: ): Promise<T> {
131:   const { workspace, session } = resolveContext(payload)
132:   const transport = await connectSocket(socketPath(workspace)).catch(() => null)
133:   if (!transport) return fallback
134:   const client = createDaemonClient(transport)
135:   try {
136:     await client.sessionOpen(
137:       ccAdapterInfo(
138:         session,
139:         workspace,
140:         resolveSessionHandle(),
141:         resolveResume(payload),
142:         resolveProfile(),
143:       ),
144:     )
145:     return await fn(client, session)
146:   } catch {
147:     return fallback
148:   } finally {
149:     client.dispose()
150:     transport.close()
151:   }

au-mcp-adapter-cc: src/bridge.ts, lines 217–223

Whole-blob SHA-256: 5b097ee946e3b9a5a7bf90a8ae7b5a86684448b287a963c58fae01f2d4eba315. Revision: 333b2117f338cd4815eeeb9ab7b3932b29fa7d62.

217: /** Ask the daemon to decide a pending action. Allows by default if unreachable. */
218: export async function mediateAction(
219:   payload: Record<string, unknown> | null,
220:   action: PendingAction,
221: ): Promise<Decision> {
222:   return withSession(payload, (client, session) => client.mediate(session, action), { kind: 'allow' })
223: }

au-mcp-adapter-cc: src/mcp-server.ts, lines 177–186

Whole-blob SHA-256: 060deba67ad41920becdc93a7d2d33e750699853e121a1347fdbcd9a2ed011ad. Revision: 333b2117f338cd4815eeeb9ab7b3932b29fa7d62.

177:     if (!client) {
178:       client = await reconnectDaemon(workspace)
179:       if (!client) return fail('au-mcp daemon not reachable for this workspace — is it running?')
180:     }
181:
182:     try {
183:       const { result, isError } = await client.invoke(handle, `mcp.${name}`, args)
184:       return ok(result, isError)
185:     } catch (e) {
186:       // A tool/engine error rides back as a normal response (isError), not a throw. A THROW

au-engine-sdk: src/client.ts, lines 667–681

Whole-blob SHA-256: b4a4c725c04bf8cca5b5e503074105111d483059f8c90d26644c1f78cb7525a1. Revision: ffeca7a745af6f8069d5d2151fc96bae964825dd.

667:   writeFile(path: string, content: string, options: WriteFileOptions = {}): Promise<TypedMutate> {
668:     return this.issueMutate({
669:       mutate: 'write_file',
670:       path,
671:       content,
672:       ...(options.expectedHash !== undefined ? { expected_hash: options.expectedHash } : {}),
673:       ...toWireStamps(options.stamps),
674:       ...toWireEnsureMixins(options.ensureMixins, options.ensureMixinsStrict),
675:       ...toWireAttribution(options.attribution),
676:     })
677:   }
678:
679:   /**
680:    * Issue a `mutate: edit_file` — exact string replacement. `oldString` must
681:    * match exactly; by default it must be unique (its own read-before-write

au-engine-sdk: src/wire.ts, lines 10–12

Whole-blob SHA-256: bd1fda901136444eb14ae63c7cf61775e1fb60abea41069c5f03add2cde36886. Revision: ffeca7a745af6f8069d5d2151fc96bae964825dd.

10:  * additive evolution (new fields, new frame types) does not.
11:  */
12: export const WIRE_SCHEMA_VERSION = 29

au-mcp-sdk: src/plugin.ts, lines 30–34

Whole-blob SHA-256: 9c4bc12654f7b7d7dcf93d537e25af5c5573104b8ece90cd28ff785a8b341407. Revision: 91209bb00cebde36e6454207a89e257f20573577.

30:  * Distinct from the wire `MCP_CONTRACT_VERSION` (the client<->daemon protocol version):
31:  * the two contracts version INDEPENDENTLY, though both are `0` today. See
32:  * [[decision - 2609091649 - the mcp wire and plugin contracts enforce exact-equality, loud fail-closed with no override::au-harness]].
33:  */
34: export const PLUGIN_CONTRACT_VERSION = 0

au-mcp-sdk: src/wire.ts, lines 10–12

Whole-blob SHA-256: b2852a48ed80a4752fbb6119abe96872c29a0bf64a7a8a425289f2e8b83a6c2e. Revision: 91209bb00cebde36e6454207a89e257f20573577.

10:
11: /** The contract version; handshake is exact integer equality. */
12: export const MCP_CONTRACT_VERSION = 0

au-engine: crates/au-engine/src/serve.rs, lines 130–132

Whole-blob SHA-256: dcd7f1ac12dce9066078741081c8cc8cf01faa6a0296dd4bbdb929db4f454ea0. Revision: 46f12389d4d4833fed51640a48cf938516433106.

130: /// daemon's reported `schema_version` against the version this binary compiles,
131: /// and flag a stale process speaking an old wire.
132: pub const SCHEMA_VERSION: u32 = 29;

au-engine: crates/au-engine/src/serve.rs, lines 3944–3959

Whole-blob SHA-256: dcd7f1ac12dce9066078741081c8cc8cf01faa6a0296dd4bbdb929db4f454ea0. Revision: 46f12389d4d4833fed51640a48cf938516433106.

3944: async fn handle_mutation(handle: &EngineHandle, m: Mutation) -> serde_json::Value {
3945:     // Serialize the write pipeline: one mutation at a time across every
3946:     // connection. Held across the whole saga and rebuild, this enforces the
3947:     // single-threaded model the intent marker and crash recovery rely on. Reads
3948:     // and subscriptions never take it.
3949:     let _write = handle.write_lock().lock().await;
3950:     // Mutating while Deriving would race the initial build; the consumer
3951:     // waits for ready like it does for reads.
3952:     if handle.version().is_none() {
3953:         return serde_json::to_value(Response::not_ready()).expect("response serializes");
3954:     }
3955:     match m {
3956:         Mutation::WriteFile(args) => {
3957:             let write_path = match resolve_mutation_target(handle, &args.path) {
3958:                 Ok(t) => t,
3959:                 Err(frame) => return frame,

au-engine: crates/au-engine/src/serve.rs, lines 3980–3987

Whole-blob SHA-256: dcd7f1ac12dce9066078741081c8cc8cf01faa6a0296dd4bbdb929db4f454ea0. Revision: 46f12389d4d4833fed51640a48cf938516433106.

3980:             let outcome = tokio::task::spawn_blocking(move || {
3981:                 let ((written, mixins), commits) =
3982:                     apply_saga(&ShellGit, &marker_dir, &plan, summary, false, || {
3983:                         let written = crate::mutate::write_file(
3984:                             &blocking_path,
3985:                             &args.content,
3986:                             args.expected_hash.as_deref(),
3987:                         )?;

au-engine: crates/au-engine/src/mutate.rs, lines 105–139

Whole-blob SHA-256: dd84a3b3d9b264f8a558c1b011894318f2be321a14aab432d68b86d34df6583f. Revision: 46f12389d4d4833fed51640a48cf938516433106.

105: pub(crate) fn write_file(
106:     target: &Path,
107:     content: &str,
108:     expected_hash: Option<&str>,
109: ) -> Result<ContentHash, MutationReject> {
110:     if let Some(expected) = expected_hash {
111:         match std::fs::read(target) {
112:             Ok(current) => {
113:                 let current_hex = hash_hex(ContentHash::of(&current));
114:                 if current_hex != expected {
115:                     return Err(MutationReject {
116:                         message: format!(
117:                             "expected_hash mismatch: the file changed since it was read \
118:                              (current {current_hex})"
119:                         ),
120:                         detail: Some(serde_json::json!({ "current_hash": current_hex })),
121:                     });
122:                 }
123:             }
124:             Err(_) => {
125:                 return Err(MutationReject::new(
126:                     "expected_hash given but the file does not exist",
127:                 ));
128:             }
129:         }
130:     }
131:     if let Some(parent) = target.parent() {
132:         if let Err(e) = std::fs::create_dir_all(parent) {
133:             return Err(MutationReject::new(format!(
134:                 "cannot create parent directories: {e}"
135:             )));
136:         }
137:     }
138:     std::fs::write(target, content)
139:         .map_err(|e| MutationReject::new(format!("cannot write {}: {e}", target.display())))?;

Namespaced-path probe

This is a manual manifest-routing/object-presence probe, not an implemented bundle resolver or a workflow trial. The rule tested is: a normalized relative POSIX path starts with an approved component name; strip that first segment, then look up the remaining committed blob at that component’s full pin. No fallback search.

Case Local observation or required rejection
au-mcp/README.md Remaining path is a blob at the named pin; shared basenames are disambiguated
au-mcp-sdk/README.md Remaining path is a blob at the named pin; shared basenames are disambiguated
au-mcp-core/README.md Remaining path is a blob at the named pin; shared basenames are disambiguated
au-mcp/../au-mcp-core/README.md Normalized-path predicate: False; reject before object lookup
/au-mcp/README.md Normalized-path predicate: False; reject before object lookup
au-mcp//README.md Normalized-path predicate: False; reject before object lookup
au-mcp/./README.md Normalized-path predicate: False; reject before object lookup
unknown/README.md Not a manifest member; reject, do not search sibling repositories
au-mcp/missing-scope-probe.ts Object lookup below confirms absent blob; reject as nonexistent anchor
Explicit revision 0000000000000000000000000000000000000000 for au-mcp/README.md Unequal to selected pin; reject before lookup even if the quote text could match
au-mcp/README.md under the current single au-mcp source No such prefixed committed blob; the existing resolver does not supply namespace routing

A read-only Python probe of the current GitPin.attribution_error rejected explicit revision 0000000000000000000000000000000000000000 for README.md with attribution uses revision 0000000000000000000000000000000000000000, expected 3ddc3b62ed0b339a89a57f11026676df18d3b2eb. It also rejected an au-mcp-core blob URL against the au-mcp pin with attribution uses repository https://github.com/arsumbris/au-mcp-core, expected https://github.com/arsumbris/au-mcp. Path-only attribution was accepted, and the current is_normalized_relative rejected all four escape/redundancy cases above. This does not test bundle dispatch. The prefixed-path row is an object-presence check, not a workflow acceptance result. Missing approved repository objects must leave affected quotations unverified during retained reading and prevent live acceptance/publication, not resolve against a different member. Symlink blobs must never authorize following another filesystem root.