docs: define committed DOCX citation and access contract
This commit is contained in:
1 parent
e36bce3526
commit
15e91538f2
1 file changed
+440
@@ -0,0 +1,440 @@
|
||||
# Q195 O2 W4: committed DOCX citations and access
|
||||
|
||||
Design for Gitea #1799, assignment sage-3060/mos-1360. Owner Rocko;
|
||||
independent reviewer Megavolt. Normative parent: `e36bce352`,
|
||||
`docs/plans/2026-10-21_q195-writing-editor.md`, blob
|
||||
`322ec50f2669cffc0f8256d6beffdaccc4d8dba8`, D12, D24-D26, W4 and W4b.
|
||||
This is a proposed implementation contract, not access enablement or a parser
|
||||
security PASS. Reviewer owns the separate verdict file; the author does not
|
||||
write an approval there.
|
||||
|
||||
## 1. Gates and compatibility
|
||||
|
||||
W4b implements this contract only after independent W4 APPROVE and Sage's
|
||||
accepted implementation brief, S0 and its other parent gates. A release must
|
||||
ship extractor, resolver, authorization and sharing together; no partial
|
||||
agent read rollout. Missing server readiness refuses every O2 read, resolve,
|
||||
share and flag. A human setting, pin or request field cannot assert readiness.
|
||||
W5 owns flags, not chapter writes. No agent execute, edit, replace, save,
|
||||
tracked-change or comment-writing operation is introduced.
|
||||
|
||||
Q36 / Vikunja 562 is OPEN. Production Markdown adoption remains closed under
|
||||
mos-1358, independently of whether this design is approved. If Jason approves
|
||||
superseded notices, a separate C2 reader/reply row must implement and pass
|
||||
them before adoption; otherwise Mos records Jason's disposition before any
|
||||
adoption brief proceeds. This document neither implements nor decides notices.
|
||||
|
||||
Existing C2 Markdown, its line-cite schema, library grants and supporting-doc
|
||||
writer remain unchanged. An authorized old Markdown original is historical
|
||||
source, never the current DOCX or a permission to its later edits. No DOCX
|
||||
paragraph is called a line. Following an adopted source from a C2 read,
|
||||
indexing it as Markdown, changing C2 replies or a synchronized Markdown
|
||||
projection requires a separately approved Case 2 amendment through Mos/Jason.
|
||||
W4b must test old C2 fixtures unchanged alongside the new discriminated DOCX
|
||||
contract. It may migrate only new O2 records, with a named schema edge Sage
|
||||
allocates; it must not reinterpret existing line citations in place.
|
||||
|
||||
## 2. Trust and document identity
|
||||
|
||||
The host reader alone opens `<dataRoot>/writing/<business>/<project>/`.
|
||||
Business, project and document are opaque IDs resolved through the host's
|
||||
owned manifest, never request paths. Each document has a stable ID and one
|
||||
canonical DOCX path; a rename does not change its identity. A copy/new import
|
||||
gets a new document ID and no inherited grant, share, pin or cache authority.
|
||||
Origin metadata is evidence only. Copying a source does not launder access.
|
||||
|
||||
A read snapshots one reachable committed manifest and its DOCX blob from the
|
||||
same commit. It never combines HEAD's manifest with another commit's DOCX.
|
||||
The reader accepts only server-resolved commits reachable from that document's
|
||||
published history; arbitrary git objects and another document's same blob
|
||||
are not selectable. Historical access also requires the document's current
|
||||
live authorization. Removed history, missing blobs or missing extractor
|
||||
revisions refuse; there is no worktree, export or engine-memory fallback.
|
||||
The commit/blob field is a locator, not an authority token.
|
||||
|
||||
The writer's successful commit publishes atomically before Saved. A latest
|
||||
lookup begun after that receipt resolves that published commit, not an old
|
||||
cache entry. A lookup already in progress remains internally on its captured
|
||||
commit. Return the captured version explicitly; never label it latest after
|
||||
observing a newer publish. Recheck live grants immediately before returning
|
||||
content, not just before asynchronous extraction.
|
||||
|
||||
## 3. Exact wire schema, version 1
|
||||
|
||||
All objects reject unknown keys. IDs are nonempty ASCII `[A-Za-z0-9_-]{1,128}`;
|
||||
commits/blobs are lowercase 40-hex Git object IDs in the repository's current
|
||||
SHA-1 object format. A future object format requires a new schema revision.
|
||||
Digest values are lowercase 64-hex SHA-256. Integers are safe nonnegative
|
||||
JSON integers. No floats, coercion, duplicate JSON keys or implicit defaults
|
||||
except the reading view described below. These are proposed O2-only records.
|
||||
|
||||
A locator is exactly:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "docx-range", "version": 1,
|
||||
"business": "acme", "project": "book", "document": "chapter_1",
|
||||
"commit": "<40 lowercase hex>", "blob": "<40 lowercase hex>",
|
||||
"extractor": {"id": "mosaic-docx-text-v1", "revision": "<64 lowercase hex>"},
|
||||
"part": {"class": "body", "name": "word/document.xml", "story": "main"},
|
||||
"paragraph": {"ordinal": 0, "xmlId": null},
|
||||
"start": 0, "end": 5, "textView": "final", "quote": "Hello",
|
||||
"quoteDigest": "<sha256 of UTF-8 quote>"
|
||||
}
|
||||
```
|
||||
|
||||
Part triples are exact: body/main uses the body part; footnote/endnote/comment
|
||||
uses its class-matching part and a canonical nonnegative decimal story ID
|
||||
(`0` or `[1-9][0-9]*`), never a user path. Paragraph `xmlId` is JSON null
|
||||
when absent. A textView is always recorded in every locator; there is no
|
||||
resolver default. quote is a nonempty valid Unicode string bounded to 16,384
|
||||
code points and 64 KiB UTF-8. Unknown enum values or unexpected types refuse.
|
||||
|
||||
`paragraph.ordinal` is the zero-based paragraph index within the selected
|
||||
story, in deterministic XML document order, including empty paragraphs and
|
||||
table-cell paragraphs. `xmlId` is null or the literal eight-hex `w14:paraId`,
|
||||
canonicalized uppercase. Ordinal is always present: paraId alone is not
|
||||
unique or stable enough. If provided, xmlId must equal the selected node's
|
||||
attribute; missing, duplicated or mismatched IDs do not trigger a search.
|
||||
Thus identical paragraphs remain distinct. `start < end <= codePointLength`
|
||||
selects one paragraph, end exclusive. A multi-paragraph selection is an
|
||||
ordered nonempty list of these ranges, with no cross-paragraph offsets.
|
||||
No auto-merging or silent clipping. Empty paragraph references may be shown
|
||||
in a reading page but cannot manufacture a nonempty quote locator.
|
||||
|
||||
`extractor.revision` is the digest of a canonical build manifest containing
|
||||
extractor source blob IDs, projection rules revision and locked parser/ZIP
|
||||
library versions. The release pins that manifest and independently verifies
|
||||
its digest. It is not caller-supplied executable code or a mutable version
|
||||
label. Resolver requires that exact available revision; changed extraction
|
||||
rules create a new revision, never silently reinterpret an old locator.
|
||||
|
||||
A paragraph extraction record is exactly:
|
||||
|
||||
```json
|
||||
{
|
||||
"part": {"class": "footnote", "name": "word/footnotes.xml", "story": "7"},
|
||||
"paragraph": {"ordinal": 0, "xmlId": null},
|
||||
"textView": "final", "text": "Note text", "length": 9,
|
||||
"textDigest": "<sha256 UTF-8 text>",
|
||||
"anchors": [], "wikiTokens": []
|
||||
}
|
||||
```
|
||||
|
||||
Anchors have the exact shape `{kind,story,offset}`; allowed
|
||||
kinds are `footnote-reference`, `endnote-reference`, `comment-start`,
|
||||
`comment-end`, `comment-reference`; story is a canonical decimal signed XML
|
||||
ID string, offset a code-point boundary in this record. They never grant
|
||||
access to their target. wikiTokens are exact source slices matching the
|
||||
literal syntax in section 5, not instructions or external links. All text
|
||||
and metadata are untrusted document data. Part/story IDs, excerpts and
|
||||
comment content never enter system prompts as instructions.
|
||||
|
||||
## 4. Story classes and package validation
|
||||
|
||||
Version 1 accepts transitional WordprocessingML namespace
|
||||
`http://schemas.openxmlformats.org/wordprocessingml/2006/main`, relationships
|
||||
`http://schemas.openxmlformats.org/package/2006/relationships`, office
|
||||
relationship types rooted at
|
||||
`http://schemas.openxmlformats.org/officeDocument/2006/relationships/`, and
|
||||
`w14:paraId` from `http://schemas.microsoft.com/office/word/2010/wordml`.
|
||||
Prefixes are irrelevant; expanded namespace/local names decide. Strict OOXML
|
||||
and unknown alternate-content choices refuse until a separately pinned
|
||||
extractor supports them; never guess equivalent namespaces.
|
||||
|
||||
| Class | Exact part names | Story selector | Agent exposure |
|
||||
| --- | --- | --- | --- |
|
||||
| body | word/document.xml | main | final by default, explicit grant |
|
||||
| footnote | word/footnotes.xml | w:footnote/@w:id | own story plus owning document grant |
|
||||
| endnote | word/endnotes.xml | w:endnote/@w:id | own story plus owning document grant |
|
||||
| comment | word/comments.xml | w:comment/@w:id | only explicit comment scope |
|
||||
| layout | word/headerN.xml, word/footerN.xml (N positive decimal) | none | denied in v1 |
|
||||
| supporting | separately declared non-story document | separate identity | C2 tools/scopes only; no DOCX scope inheritance |
|
||||
| opaque | media, styles, numbering, theme, fonts, custom XML, embedded objects | none | no raw bytes through O2 reader |
|
||||
|
||||
Body/note/comment ownership comes solely from relationships in that committed
|
||||
package, not filenames supplied by a request. A note/comment belongs to one
|
||||
DOCX, never the book or an adjacent chapter. Reserved separator/continuation
|
||||
notes (negative IDs and type separator/continuationSeparator) are structural,
|
||||
not readable stories. Duplicate story IDs, dangling references or overlapping
|
||||
ambiguous comment ownership refuse extraction. Unreferenced comment/note
|
||||
stories are not agent-readable. Cross-paragraph comment anchors may be
|
||||
recorded, but never concatenate their text with body text.
|
||||
|
||||
The root `_rels/.rels` must contain exactly one internal officeDocument
|
||||
relationship to `word/document.xml`. Its matching content type is
|
||||
`application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml`.
|
||||
Optional footnotes/endnotes/comments each have at most one matching internal
|
||||
relationship and their matching `...wordprocessingml.{footnotes,endnotes,comments}+xml`
|
||||
content type. Their relationship targets must be the exact part names above.
|
||||
Reject normalized-path aliases, absolute paths, traversal, encoded separators,
|
||||
backslashes, duplicate ZIP names, symlink entries, encryption and overlapping
|
||||
ZIP data. Never extract ZIP entries onto the filesystem.
|
||||
|
||||
Only internal relationships of types officeDocument, styles, numbering,
|
||||
settings, webSettings, fontTable, theme, image, header, footer, footnotes,
|
||||
endnotes and comments are accepted. Resolve internal targets relative to the
|
||||
owning part within the package, reject escape and validate existence/type.
|
||||
Reject every external relationship, hyperlink relationship, altChunk,
|
||||
attachedTemplate, OLE, package embedding, VBA/macro content type or executable
|
||||
part. Textual literal wiki links need no relationship. Unsupported rich
|
||||
constructs refuse the agent projection, not the human save: the human can
|
||||
retain a full document without it becoming readable to agents.
|
||||
|
||||
Parse XML without DTDs, external entities, XInclude or network/file resolution.
|
||||
Reject DOCTYPE/entity declarations. Bound before allocation: compressed blob
|
||||
32 MiB, total expanded bytes 128 MiB, each entry 16 MiB, 2048 entries,
|
||||
expansion ratio 100:1, XML depth 128, 1,000,000 nodes, 100,000 paragraphs,
|
||||
1,000,000 code points per paragraph and 10,000,000 per document. Extraction
|
||||
has a 5 s host deadline and bounded worker memory of 256 MiB. Exceeding any
|
||||
bound yields `editor-document-unsupported`, no truncated success. These are
|
||||
initial server safety ceilings, not grants or business quota settings;
|
||||
changing them requires reviewed parser evidence. CRC/size mismatches refuse.
|
||||
|
||||
## 5. Deterministic reading views
|
||||
|
||||
`textView` enum is `final`, `original`, `markup`. Default requests use final.
|
||||
Final is the accepted-reading projection: include w:t in normal runs and
|
||||
w:ins/w:moveTo; exclude w:del/w:moveFrom, including w:delText. Original does
|
||||
the reverse. This does not accept/reject changes or modify the DOCX. Markup
|
||||
includes both in source order and returns explicit revision spans with kind,
|
||||
ID and code-point offsets; deletion/source text is data, not an instruction.
|
||||
Original/markup and comments require separately enabled scopes; final body
|
||||
permission alone cannot reveal hidden deletions or comment text. Revision
|
||||
author/date metadata is omitted from agent responses in v1.
|
||||
|
||||
Walk direct story paragraphs and table cells in XML order, including nested
|
||||
tables in their containing cell order. Paragraph ordinals do not change with
|
||||
view. Concatenate visible text across adjacent runs with no invented spaces.
|
||||
Preserve decoded Unicode as stored: no NFC, case folding or whitespace
|
||||
collapse. w:tab yields U+0009; w:br/w:cr yields U+000A; xml:space text remains
|
||||
literal. Offsets count Unicode scalar values, not UTF-16 units or graphemes;
|
||||
unpaired surrogates/invalid XML characters refuse. Field instructions,
|
||||
hidden/vanished text and text boxes are not silently omitted: their presence
|
||||
in a requested story makes v1 projection unsupported. Field results and
|
||||
unknown semantic text-bearing elements likewise refuse until specified.
|
||||
The projection traverses only these containers in the w namespace:
|
||||
body, footnote, endnote, comment, tbl, tr, tc, p, r, ins, del, moveFrom,
|
||||
moveTo, smartTag and sdtContent. A document root wraps body; note/comment
|
||||
part roots wrap their corresponding stories. sdt may contain sdtPr (ignored
|
||||
properties) and sdtContent only. Hyperlink containers are unsupported even
|
||||
without a relationship. Unknown text-bearing containers refuse. Nontext
|
||||
subtrees pPr, rPr, tblPr, tblGrid, trPr, tcPr and sdtPr may be ignored only
|
||||
if they contain no story text elements, field codes, external references,
|
||||
objects or unknown namespaces. Structural leaves bookmarkStart/bookmarkEnd,
|
||||
proofErr, lastRenderedPageBreak and the reference marks below contribute no
|
||||
text. w:sectPr is layout-only, with relationship targets validated normally.
|
||||
Formatting-property descendants are a closed W4b release artifact: its full
|
||||
expanded-name list is included in the extractor manifest and independently
|
||||
reviewed before enablement; any name absent from that list refuses. This
|
||||
artifact can narrow the allowed property subtrees, never add a text-bearing
|
||||
container to this v1 contract. Missing artifact is no readiness, not a
|
||||
permissive descendant-text scraper.
|
||||
|
||||
Reference marks contribute anchors at the current text boundary, not invented
|
||||
note numbers or inline comment bodies. Each note/comment is separately paged.
|
||||
A quote cannot straddle a view, paragraph or story. Extract literal
|
||||
`[[target]]`, `[[target|label]]`, `[[target#heading]]` and `![[target]]`
|
||||
after concatenating visible runs within one paragraph, so split-run tokens
|
||||
survive formatting changes. Preserve complete literal bytes as UTF-8 after
|
||||
XML decoding and code-point start/end; never follow OS paths or external URLs.
|
||||
Targets resolve only via the host's owned-document resolver, with independent
|
||||
authorization; unresolved targets stay literal and marked unresolved in human
|
||||
UI. No link parsing grants reads. Nested/unclosed bracket sequences remain
|
||||
plain text, and tokens cannot cross paragraph/view boundaries.
|
||||
|
||||
## 6. Reading APIs, verification, paging and staleness
|
||||
|
||||
These are proposed typed tool schemas over the existing runner socket, not
|
||||
native WordCraft MCP. Caller/session/business come from the bound broker.
|
||||
Agents cannot override them using object fields.
|
||||
|
||||
- `editor_read_committed({project,document,version,textView,part,cursor,limit})`:
|
||||
project, document, version and part are required; only textView, cursor and
|
||||
limit may be omitted. version is exactly `{mode:"latest"}` or
|
||||
`{mode:"commit",commit}`; part is
|
||||
one class/name/story triple above; textView defaults final; cursor is null
|
||||
on first page, limit 1..100 (default 20). No path, blob or draft argument.
|
||||
- `editor_resolve_citation({locator})`: verify the exact immutable range;
|
||||
it does not search for a nearby quote. Return exact quote and locator plus
|
||||
`currentCommit` and `stale` after current authorization.
|
||||
- `editor_flag` is W5's metadata-only write; W5 must validate the same
|
||||
locator before storing a flag and may not write into the document.
|
||||
|
||||
Read response is exactly `{version:1, business,project,document,commit,blob,
|
||||
extractor,textView,part,rows,nextCursor,currentCommit,stale}`. Each row has the
|
||||
extraction record above. The public envelope contains no absolute paths or
|
||||
raw package attributes. `nextCursor` is null at end; `currentCommit` is the
|
||||
current published document commit; stale is exactly commit != currentCommit.
|
||||
A resolver response is exactly `{version:1,locator,currentCommit,stale}`:
|
||||
the verified quote is the locator's quote, with no separate unverified text.
|
||||
There is no default "first part" or automatic descent into notes/comments.
|
||||
Markup additionally requires `revisionSpans` on each
|
||||
row: array of `{kind,id,start,end}`, kind insert/delete/move-from/move-to,
|
||||
with null-free canonical decimal IDs and valid boundaries; other views omit
|
||||
that key. Limits cap output at 64 KiB UTF-8 JSON per response. Never split a
|
||||
paragraph silently: if one row exceeds the page budget, return
|
||||
`editor-range-too-large`; a caller may request a checked subrange through a
|
||||
separately bounded citation, not receive a truncated paragraph masquerading
|
||||
as complete. Response overflow never returns partial data with success.
|
||||
|
||||
Cursor is a host-authenticated opaque token binding principal, session,
|
||||
project/document, commit/blob, extractor, view, part, next ordinal, grant
|
||||
revision and expiration (5 minutes). It carries no plaintext content.
|
||||
Tampering, reuse by another session, wrong request parameters or a revoked
|
||||
scope refuses. A latest cursor continues its captured snapshot with stale
|
||||
metadata if a save intervenes; starting again with null cursor returns the
|
||||
new publication. Cursor is not a capability and grants are rechecked every
|
||||
page. Repeated ordinal ranges are stable within one committed projection.
|
||||
|
||||
Resolver steps: authorize current principal and historical-view scope; load
|
||||
that commit's manifest/blob; validate package; run the exact pinned extractor;
|
||||
select exact part/story/ordinal and optional XML ID; select code-point range;
|
||||
require literal quote equality and SHA-256(UTF-8 quote) equality. Require both
|
||||
blob and commit membership, not digest alone. Any mismatch refuses
|
||||
`editor-citation-invalid` without guessed text. After newer publication the
|
||||
old valid locator returns `stale:true` with old text; it never auto-relocates
|
||||
to identical paragraphs or another note. Jump opens that committed view with
|
||||
an explicit old-version label. If current grant no longer allows it, even
|
||||
old quote resolution refuses and the UI hides cached content.
|
||||
|
||||
Errors: `editor-context-unavailable` for missing readiness; `editor-access-refused`
|
||||
for absent ownership/grant/share (same response for nonexistent or foreign IDs);
|
||||
`editor-document-unsupported` for parser limits or unsupported features after
|
||||
authorization; `editor-version-unavailable` for authorized missing history or
|
||||
extractor; `editor-citation-invalid` for locator/quote mismatch;
|
||||
`editor-cursor-invalid` for invalid paging; `editor-range-too-large` for size.
|
||||
Return no source text, XML, path or untrusted parser error in refusals.
|
||||
|
||||
## 7. Grants, sharing and all read surfaces
|
||||
|
||||
Access is the intersection of bound role tool grant, human-configured
|
||||
business/project/document scope, principal's live project/universe rights,
|
||||
requested part/view scope, server readiness and a valid share context for
|
||||
this conversation. No wildcard across businesses/universes. Human/session
|
||||
identity comes from existing trusted transport; a request cannot name a human.
|
||||
The six creative role tool declarations grant the *operation*, never every
|
||||
chapter. An agent's own supporting-document writer grants no story access.
|
||||
|
||||
Human-only sharing policy defaults `visible-chip-per-send`; the chip is
|
||||
visible and removable and binds document, committed version and selected
|
||||
verified locators. Before readiness it says editor context unavailable and
|
||||
sends no document ID, title, path, selection, quote or source metadata.
|
||||
The server resolves selection against the saved projection; if it cannot
|
||||
map exactly (including a live edited selection), refuse context attachment.
|
||||
Never send unsaved text to try to find a match. Free chat can still send with
|
||||
no editor context if the human removes the unavailable chip.
|
||||
|
||||
A successful send creates a server-owned immutable share record scoped to
|
||||
that message/conversation, human principal, document and exact committed
|
||||
ranges/views. It narrows the live grant; it does not widen it or authorize a
|
||||
later version. Every subsequent lookup supplies the server-resolved share
|
||||
context and intersects current grants. A future send requires another visible
|
||||
chip under the default policy. A human may explicitly change sharing to
|
||||
`off` or `conversation-current`: off revokes future reads; conversation-current
|
||||
permits the named conversation's authorized latest lookups until human
|
||||
revocation/session end, with a persistent visible indicator. Such an explicit
|
||||
policy change cannot add document scopes or hide the indicator. Pins only
|
||||
organize human UI; they never create a share record or turn on sharing.
|
||||
A range share permits only its ranges, not an entire paragraph/page or comments.
|
||||
Latest whole-document reads require a whole-document share, never a range chip.
|
||||
|
||||
Historical exact shares resolve their old citations while live authorization
|
||||
holds. A successful save makes the next authorized *latest* lookup use its
|
||||
new commit, but never silently upgrades an exact earlier share. The UI says
|
||||
"Saved newer version; share again" where only an old range is shared. No
|
||||
future edit is disclosed on the strength of an earlier send. Share records
|
||||
are resolved by broker session/context, not caller-minted IDs.
|
||||
|
||||
Before returning raw conversation/session history, replaying a prompt,
|
||||
resuming/forking a worker or exporting an agent transcript, reauthorize each
|
||||
O2 attachment/content block. Keep source text out of generic raw session
|
||||
stores and logs: persist opaque checked references, not rich prompt text.
|
||||
Host prompt assembly resolves content in memory for the authorized turn.
|
||||
Agent harness transcripts containing expanded content must stay host-owned,
|
||||
outside worker/generic roots, encrypted or withheld from raw session tools;
|
||||
W4b must prove the selected harness does not persist readable O2 text into a
|
||||
worker-readable store. Otherwise that harness's editor tools remain disabled.
|
||||
Revocation cannot erase an agent's memory or already delivered output; it
|
||||
prevents subsequent fetch/replay, and the UI must not promise retrospective
|
||||
secrecy. Copying an authorized quote into free agent prose is not a new grant:
|
||||
O2-derived stored outputs retain the same provenance/access envelope.
|
||||
This is conservative turn-level provenance, not content recognition: every
|
||||
output of a turn receiving O2 context, and any resumed/forked descendant,
|
||||
inherits the union of those document scopes. It can be emitted only into a
|
||||
host-controlled destination enforcing that envelope. Generic supporting-doc
|
||||
writes, git writes, exports and arbitrary outgoing tools from that turn
|
||||
refuse unless their destination has the identical enforcement; normal C2
|
||||
turns without O2 context keep their existing tool semantics. W4b must enforce
|
||||
this broker-side before dispatch, not ask an agent to self-label a quote.
|
||||
If the runner cannot isolate that tool set for an O2-context turn, it cannot
|
||||
receive O2 context. The separate story-write ban always applies.
|
||||
|
||||
All O2-derived exports, caches, extraction indexes, previews, temporary files,
|
||||
source copies and git history are classified with their document, outside
|
||||
generic read/grep roots and outside Case 2 library mappings. No raw O2 git
|
||||
clone/read grant; no source copy under a worker's project root. Generic git
|
||||
or shell tools cannot reach it through shared parent mounts, symlinks,
|
||||
hardlinks, alternate worktrees, object alternates or exports. Read-only is
|
||||
insufficient: no store mount in workers. Validate canonical roots, reject
|
||||
symlink components, and enforce ownership/mode and sandbox mount closure.
|
||||
A new import/copy needs fresh document grants and sharing. Don't expose
|
||||
manifest names/document lists through metadata routes before authorization.
|
||||
Native engine routes remain human-only and cannot be proxied by agent tools.
|
||||
|
||||
W4b is blocked if any raw session or generic surface cannot enforce these
|
||||
rules. Do not compensate by UI hiding, retroactively classifying all Case 2
|
||||
Markdown as secret or widening its schema. Store and bus migrations are
|
||||
reviewed together; revoke/invalidate caches by grant revision and verify live
|
||||
scope again before emission. Cache keys include document, commit/blob,
|
||||
extractor, part and view; shared extraction cache is never an authorization
|
||||
cache. Host audit events contain IDs/digests/status only, never content.
|
||||
|
||||
## 8. Acceptance matrix and release evidence
|
||||
|
||||
Each row needs a named fixture/test and a mutant or bypass probe in W4b's
|
||||
packet. Independent fixtures construct OOXML directly and recompute Unicode
|
||||
slices/digests outside the production extractor. Security checks fail closed;
|
||||
no fixture-only readiness flag may exist in production APIs.
|
||||
|
||||
| ID | Evidence required | Negative control / bypass |
|
||||
| --- | --- | --- |
|
||||
| A01 | Exact body quote independently recomputed from commit/blob/revision | change quote, digest, blob, commit or extractor; each refuses |
|
||||
| A02 | Two identical paragraphs and notes have different locators | no content-search relocation; swapped ordinal/story refuses |
|
||||
| A03 | XML ID absent/duplicate and ordinal behavior | missing/mismatched paraId cannot silently choose another node |
|
||||
| A04 | Emoji, combining characters, tabs, breaks and split runs | UTF-16 offsets, normalization and whitespace collapse fail |
|
||||
| A05 | Literal split-run wiki forms retained | no path/URL fetch; malformed/cross-paragraph tokens stay text |
|
||||
| A06 | Final/original/markup independently verified with moves/deletions | final-only grant cannot read deletion/comment; no document rewrite |
|
||||
| A07 | Body/note/endnote/comment exact ownership | orphan/dangling/cross-document references and duplicate IDs refuse |
|
||||
| A08 | Package allowlist, aliases, CRC, XML/ZIP limits | external rel, macro, traversal, symlink, entity, ZIP bomb and timeout refuse |
|
||||
| A09 | Commit snapshot and publication ordering | next latest after Saved gets new commit; no worktree/draft fallback |
|
||||
| A10 | Historical resolve stale label | old quote stays old; removed grant/history refuses without text |
|
||||
| A11 | Pagination complete/deterministic and bounded | tampered/foreign/revoked cursor and oversized row refuse |
|
||||
| A12 | Cross-user/business/project/universe denial | guessed IDs, same blob elsewhere and copied document grant nothing |
|
||||
| A13 | Visible range chip exact boundaries and default off before gate | no selection/title/path metadata before readiness; draft mapping refused |
|
||||
| A14 | Every lookup intersects live grant and share | pin, old send, revoked scope and range-to-whole-document escalation fail |
|
||||
| A15 | Whole-document latest share explicit; save rechecked | exact share cannot reveal future edits; persistent policy visible |
|
||||
| A16 | Generic file/search/git/export/index paths denied | source copy, symlink, hardlink, alternate object root and new O2 copy probes |
|
||||
| A17 | Raw session, fork/resume and transcript boundaries | revoked context cannot replay via raw tool or harness history |
|
||||
| A18 | Agent tools exactly read/resolve/flag after gates | native MCP, execute, save, replacement and story comment writes refuse |
|
||||
| A19 | Existing C2 Markdown reads/line cites unchanged | no DOCX-as-lines conversion or redirect following adoption |
|
||||
| A20 | Adoption gate before/after readiness and Q36 disposition | absent/revoked server readiness and required notice readiness refuse |
|
||||
| A21 | Human full-feature save remains separate | unsupported projection yields no agent context, never lossy human save |
|
||||
| A22 | Scope revocation during extraction | final pre-emission check prevents async stale-authority leak |
|
||||
|
||||
Implementation acceptance includes writing, bus, harness, business, WebUI,
|
||||
seat mount/import closure and stack-copy/release fixtures; explicit production
|
||||
adoption refusal/lift tests; independent parser/policy/tool review; G1 and
|
||||
Sage's allocated schema edge. Evidence must name skips and refuse enablement
|
||||
when an acceptance guard was not exercised. W4's review alone lifts nothing.
|
||||
|
||||
## 9. Handoff and unresolved decisions
|
||||
|
||||
Megavolt reviews this separate document and records a verdict in
|
||||
`docs/reviews/2026-10-22_q195-docx-citations.md`. Revisions repeat direct
|
||||
commit/blob handoff to Megavolt and Sage. Q36 remains with Jason through Mos;
|
||||
no adoption, C2 reader/reply notice change or schema reinterpretation ships
|
||||
on this document. W4b's brief must allocate ownership for every surface in
|
||||
A16/A17 before code: an unowned bypass is a blocker, not a deferred security
|
||||
note. No live files, editor source imports, parser execution or tests were
|
||||
used to write this design.
|
||||
Reference in new issue
Block a user