CR identifier. CR-2026-XXX (next number in sequence; CR drafter confirms against engine repo docs/phase-crs/ listing before archival).
Version. 0.2
Date. 2026-05-14
Status. Drafted; awaiting Operator approval. Supersedes v0.1. Pre-flight Step 0 already run (unchanged from v0.1; V0–V11 evidence baseline holds); CC consumes this CR plus phase-59-step-0-findings-v0_1.md as the evidence baseline.
Author. Claude.ai (CR re-drafter) on direction from Marvin Percival (DUNIN7 Operator).
Companion documents.
loomworks-phase-59-scoping-note-v0_2.md — authoritative scope; eleven P59-D items closed including new P59-D11.loomworks-phase-59-scoping-note-v0_1.md — superseded; preserved for Move 5 trajectory record.loomworks-phase-59-step-0-inspection-brief-v0_1.md — Step 0 inspection brief; V0 through V11. Unchanged at v0.2.phase-59-step-0-findings-v0_1.md (engine repo docs/phase-impl-notes/ on phase-59-step-0 branch) — Step 0 evidence; the live-codebase reading this CR rests on. Unchanged at v0.2 (V8 detection-registry evidence carries; 2f consumes the same dispatch surface 2e covers).loomworks-phase-59-cr-drafting-handoff-v0_1.md — underlying drafter checklist (§9 CRV verifications, §10 drafter checklist, §11 halt conditions, structural shape). Unchanged in substance. This CR's re-draft consumes that handoff plus the re-draft handoff below.loomworks-phase-59-cr-redraft-handoff-v0_1.md — v0.2 delta-spec; this CR follows its §5 section-by-section enumeration plus §6 preservation principle.phase-59-cr-upload-pathway-completion-v0_1.md — structural reference (preserved alongside as superseded); sections that don't change carry verbatim per re-draft handoff §6.phase-58-implementation-notes-v0_2.md (engine repo docs/phase-impl-notes/; 542 lines / 13 sections at engine 2a6a753) — Phase 58 close state.phase-58-step-3-findings-v0_1.md (engine repo docs/phase-impl-notes/) — Phase 58 step-3 findings.docs/phase-crs/ (final shipped version per Phase 58 close; likely phase-58-cr-upload-pathway-v0_5.md per the five-cycle amendment trajectory).current-status-manifest-v0_41.md — manifest baseline.loomworks-queued-directions-and-deferred-work-v0_11.md — deferred work past Phase 59.Amendment record (v0.2; pre-execution). v0.2 absorbs a correction surfaced during CR drafting by Operator review (scoping v0.2 Move 5). v0.1 scoping note compressed two distinct Operator-engagement-at-dispatch surfaces — confidence-disambiguation (engine isn't sure it dispatched correctly) and purpose-disambiguation (multiple legitimate skills fire at high confidence; the choice is the Operator's purpose) — into one (the classifier confidence model at P59-D4). v0.2 of the scoping note separates them: P59-D4 covers confidence-disambiguation only; new P59-D11 covers purpose-disambiguation. v0.2 of this CR carries the separation through: §3 expands to eleven P59-D rows; §14 sharpens its title and framing to confidence-disambiguation; new §15 (Sub-arc 2f) carries purpose-disambiguation; downstream sections renumber by +1; §19–§25 absorb test-count widening, halt-condition additions, acceptance-gate items, carry-forward additions, and methodology-candidate additions. The v0.1 CR is preserved alongside as superseded. Methodologically: v0.2 is the second-order evidence for v1-multi-pathway-collapses-inquiry-as-protocol — the inquiry, applied at scoping time, produced a compression that Operator review caught. Phase 59 build runs against v0.2 of both scoping note and CR.
Phase 59 finishes the v1 upload pathway Phase 58 started. Three sub-arcs span four segments of engine-side work plus a fifth segment that moves to Phase 60 per the Stage 4 envelope split that the Phase 59 scoping conversation produced.
The phase's two outputs:
Substrate output. Wave 7 wiring complete: credential-store consolidation; ObjectStore put_blob end-to-end; UploadEventReceived persistence; rate-limit middleware on the upload endpoint; streaming-upload optimization. The v1-image-and-audio capabilities reach first-quality through the new pathway: API-key resolution for new-pathway audio/vision/OCR; narration shaping that consumes both extraction result and accompanying Operator message; classifier confidence model with threshold-driven re-dispatch (confidence-disambiguation); multi-valid-prompt-for-process detection (purpose-disambiguation); OCR threshold model refinement; OCR class-aware parameter refinement; content-reliability warning surface. Plus the engine half of the v1-unsupported-format pathway: the CR §7.4 fallback affordance endpoint Phase 58 deferred.
Methodology output. Phase 59 is the first phase running the v1-multi-pathway-collapses-to-single-pathway inquiry as a named protocol. Phase 58 surfaced the pattern with four-instance evidence within one phase (per phase-58-implementation-notes-v0_2.md); Phase 59 codifies the inquiry as a per-item scoping-time discipline. Implementation notes will record where the protocol fit cleanly under build pressure (deliberate verdicts matched build experience) and where it surfaced new shape (drift or anticipated-vs-surprising). This is the second-instance evidence that will inform whether the protocol promotes to a named principle at the what-dunin7-is-building v0.21 consolidation slotted between Phase 59 close and Phase 60 scoping.
Scoping v0.2 added a second-order evidence point worth recording in this Purpose paragraph rather than only in implementation notes. The inquiry-as-protocol, applied at scoping time, produced a compression that Operator review caught (Move 5: v0.1 compressed confidence-disambiguation and purpose-disambiguation into one surface; v0.2 separated them as orthogonal). The protocol's vulnerability to scoping-time self-compression — and Operator review as its recovery mechanism — is itself methodology evidence. The first-application phase has now surfaced two kinds of evidence: how the protocol fits per-item discipline, and how the protocol fails as a self-applied check at scoping time. Both feed v0.21.
The originally-planned thirteen-item single-phase scope that the Phase 59 entry handoff anticipated split at Stage 4 into Phase 59 (engine; segments 1–4) plus Phase 60 (Contribution composer completeness; OL-block). The split was not in the entry handoff's scoping space — it emerged from Stage 4 envelope analysis as the cleanest line through coherent phase identities. Phase 60 inherits Phase 59's engine-side contracts; the OL surface implements the consumption.
Engine-side work only. Five sub-arc components map across four segments:
_ANCHOR_PRIORITY entry; 1d rate-limit middleware on upload endpoint; 1e streaming-upload optimization.Each component is detailed below at §7 through §19.
The following do not ship in Phase 59:
.DS_Store, Thumbs.db, etc.). Moves to Phase 60 (OL-side; packages with composer-completeness).what-dunin7-is-building — scheduled between Phase 59 close and Phase 60 scoping per P59-D10; runs parallel as a separate Claude.ai work session.create_project.md plain-terms revision, voice work on 22 other voice templates, Phase 31 / Phase 53 unification verdict, operational hygiene cluster (7 stale origin/phase-* refs), Phase 30 induction of existing engagements, engagement-creation substrate refactor residues, Companion-assisted Discovery during onboarding, standing Companion substrate, Claude Dreaming, quick-capture, mobile presence, image generation, FORAY/OVA integration, Phase 32 marketing engagement. All deferred per Stage 1.1 carry-forward survey; queued-directions v0.11 carries them.If CC finds itself implementing any of the above, it has crossed a scope boundary — halt and surface.
All eleven P59-D items closed at scoping v0.2 (ten from v0.1 plus new P59-D11). CC consumes them; does not relitigate.
| Item | Setting | Source |
|------|---------|--------|
| P59-D1 | Envelope split. Phase 59 = engine (Segments 1–4); Phase 60 = OL composer-completeness + folder filter. | Scoping v0.1 §Stage 4; emerged at Stage 4 envelope analysis |
| P59-D2 | Credential-store coexistence. Accept multi-pattern; document the two scopes (system-level via app.state; engagement-level via credential-store). New-pathway transformation skills read from credential-store; Phase 16 voice-mode endpoint continues to read from app.state.openai_api_key. | Scoping v0.1 §Stage 2.1; settled by inheritance-audit drift verdict (accept multi-pattern) |
| P59-D3 | Narration shaping. Multi-pathway restore; narration consumes both extraction result and accompanying Operator message. Empty accompanying-message path falls back to current isolation behavior — multi-pathway is additive. | Scoping v0.1 §Stage 2.2; drift verdict (restore multi-pathway) |
| P59-D4 | Classifier confidence model (confidence-disambiguation). Threshold-driven re-dispatch on low confidence (ask Operator to clarify); not multi-skill comparison. Single-skill-commit dispatch is accepted as deliberate. v0.2 clarification: this decision covers the confidence-disambiguation surface only — engine uncertainty about classification accuracy. v0.2 adds P59-D11 for purpose-disambiguation as a distinct, orthogonal surface (multiple legitimate skills at high confidence; the choice is the Operator's purpose, not engine accuracy). | Scoping v0.1 §Stage 2.2 + v0.2 §Stage 2.2 clarification; drift-justified verdict (accept single-skill-commit; add threshold-driven re-dispatch) |
| P59-D5 | Content-reliability surface. Three-state extraction result: succeeded / unreliable_succeeded / failed. The third state surfaces a warning alongside the assertion content; Phase 60's OL surface displays it. Assertion content persists in all three success cases (including unreliable); the warning is metadata, not a rejection. | Scoping v0.1 §Stage 2.2; drift verdict (restore three-state surface) |
| P59-D6 | Image-skill dispatch precedence. Settled via Step 0 V2 (Scenario A or B per findings); documentation-only resolution. CC consults phase-59-step-0-findings-v0_1.md §V2 for the verdict; implementation captures the precedence (Scenario A: both Phase 16 stub and Phase 58 narrower-key registrations live with longest-match-wins; Scenario B: Phase 58 retired the Phase 16 registration; only new skills live). | Scoping v0.1 §Stage 2.2; documentation-only resolution per inheritance audit |
| P59-D7 | UploadEventReceived registry posture. _ANCHOR_PRIORITY entry added per Phase 53 V7 evidence pattern; OBJECT_TYPE_REGISTRY entry deferred per Phase 50/52/53 precedent (typed-MemoryObject-without-migration pattern). No Alembic migration required. | Scoping v0.1 §Stage 2.1; Phase 50/52/53 precedent inherited |
| P59-D8 | Rate-limit library. slowapi per Phase 50's resolution. Phase 50 wired slowapi to the public credit-request endpoint; Phase 59 mirrors the pattern on the upload endpoint with an upload-appropriate rate value (per-call cost is higher; per-minute rate is lower). | Scoping v0.1 §Stage 2.1; Phase 50 precedent inherited |
| P59-D9 | Reserved-slot count. 4 slots; envelope-breach beyond reservation requires explicit Operator authorization per the Phase 58 v0.5 pattern. Per-segment risk allocation: 1 slot Sub-arc (i) (credential-store coexistence; streaming-upload async-context); 1 slot Sub-arc (ii) wiring (narration multi-pathway restore changes existing-output shape; v0.2: 2f's purpose-disambiguation surface adds an engine-side data shape Phase 60 must consume cleanly; same OL-contract-preservation risk family — single slot covers the 2e/2f cluster per v0.2 §Stage 4.2 reasoning); 1 slot Sub-arc (ii) quality (first-pass tuning surfacing); 1 slot standing reservation. Phase 58's two-slot reservation absorbed two routine cycles before fifth-cycle envelope-breach; Phase 59's count is doubled to anticipate new-protocol-first-application surfacing protocol shape + first-quality tuning surfacing calibration findings. v0.2 confirms slot count unchanged after 2f addition. | Scoping v0.1 §Stage 4.2 + v0.2 §4.2 reasoning |
| P59-D10 | Methodology v0.21 slot. Between Phase 59 close and Phase 60 scoping. Composer-completeness ships afterward. The slot allows Phase 59's implementation notes (recording v1-multi-pathway-collapses inquiry's second-instance application evidence) to feed v0.21 before Phase 60 scoping consumes v0.21 as a baseline. | Scoping v0.1 §Stage 4.1 |
| P59-D11 [NEW v0.2] | Multi-valid-prompt-for-process (purpose-disambiguation). When detection surfaces multiple registered skills matching at high confidence (i.e., not the P59-D4 low-confidence case; the confidence threshold is exceeded on multiple candidates), the engine emits AskOperatorForProcess shape rather than committing to a single skill. The shape carries per-candidate purpose-framing strings the Companion uses to compose an elicitation prompt. If Sub-arc 2d's accompanying-message input is present AND disambiguates purpose, 2f does not fire — the message answers the question pre-emptively. Phase 59 ships the engine-side data + contract; Phase 60 OL renders the elicitation affordance. Shape name resolved at this re-draft: AskOperatorForProcess — the existing AskOperatorToClarify shape (P59-D4 surface) establishes the "Ask Operator [preposition] [thing]" family in the codebase; AskOperatorForProcess extends that family while preserving the Operator's "prompt for process" phrasing. Considered and rejected: AskOperatorToChooseProcess (more grammatically parallel to ToClarify but loses the Operator's preposition); AskOperatorForPurpose (uses methodology vocabulary rather than Operator vocabulary — fails plain-terms-discipline since the shape name surfaces into engine-side data Phase 60 consumes). CRV-7 at execution time may surface a deeper codebase-naming-family divergence; per re-draft handoff §7 that's naming-only and absorbs in-flight unless architectural. | Scoping v0.2 §Open construction decisions; surfaced post-v0.1 by Operator review (Move 5); naming resolved at this re-draft |
All decisions traceable to scoping v0.2 (with the ten v0.1 items unchanged); no decision in this CR overrides or revises a P59-D item. If a v0.2 decision feels wrong during build, CC halts per scoping v0.2 §Halt conditions / handoff §11 — the chat doesn't override; it surfaces.
Step 0 already ran. CC produced phase-59-step-0-findings-v0_1.md on the phase-59-step-0 branch from main at engine repo docs/phase-impl-notes/. The Step 0 inspection brief (loomworks-phase-59-step-0-inspection-brief-v0_1.md) specified eleven verifications V0 through V11 covering: Phase 58 close baseline + Step 0 branch lifecycle accounting (V0); transformation skills enumeration (V1); image-skill dispatch precedence (V2 — settles P59-D6); ObjectStore put_blob method state (V3); credential-store abstraction state (V4); UploadEventReceived event class state (V5); rate-limit middleware state (V6); streaming-upload state (V7); detection registry state + classifier confidence model (V8); Companion narration current shape (V9); content-reliability surface absence (V10); CR §7.4 fallback affordance endpoint absence (V11).
This CR consumes the findings as evidence baseline. CC does not re-run Step 0 verifications V0 through V11; the build steps below cite Step 0 evidence by verification number where the implementation depends on a Step 0 finding. v0.2 adds no new Step 0 verifications — Sub-arc 2f consumes the same detection-registry dispatch surface V8 already covered. Per scoping v0.2 §Companion artifacts: "the existing evidence baseline holds; v0.2's additions don't require new Step 0 verifications (2f consumes the same detection-registry dispatch surface V8 already covered)."
Pre-flight (CR-execution time). Before Step 1 begins, CC archives this CR at engine repo docs/phase-crs/phase-59-cr-upload-pathway-completion-v0_2.md (preserves phase-59-cr-upload-pathway-completion-v0_1.md alongside as superseded per re-draft handoff §6 preservation principle). CC then runs a small CR-execution-time read confirming the substrate baseline hasn't shifted since Step 0 closure:
CR-2026-XXX (this CR's number) is the next available against engine repo docs/phase-crs/ directory listing; advance the number if taken. v0.2 note: if the v0.1 CR's number was already claimed at v0.1 archival, v0.2 reuses the same number (versioned filename, not re-numbered CR). If the v0.1 number was not claimed (e.g., v0.1 never reached archival), v0.2 takes the next available. CC consults engine repo docs/phase-crs/ for the v0.1 archival state.phase-58-upload-pathway still resolves at the commit Step 0 V0 recorded..venv/bin/pytest -q against the engine repo's main branch; confirm count is within ±2 of the Step 0 V0 figure (allowing for any post-tag docs-on-main commits).0064 still holds.phase-59-upload-pathway-completion from main on engine repo. No OL branch — Phase 59 is engine-only per scoping v0.2.If pre-flight reveals any drift greater than minor (e.g., test counts off by more than ±2; tag missing; Alembic head shifted), CC halts and surfaces; the CR re-drafter (Claude.ai) consumes the drift into a v0.3 of this CR rather than CC absorbing silently.
Engine repo only. DUNIN7/loomworks-engine at /Users/dunin7/loomworks-engine. All six sub-arc components (1a–1e; 2a–2f; 3a–3c; 4a) land in this repo.
No Operator Layer changes. DUNIN7/loomworks at /Users/dunin7/loomworks remains at the Phase 58 close state through Phase 59's life. Phase 60 carries OL-side work. Phase 59's engine-side contracts are designed to be consumed by Phase 60's OL surfaces but Phase 59 itself ships no OL code.
No marketing repo changes. Untouched in Phase 59.
No new dependencies in pyproject.toml. Step 0 V6 confirms slowapi is already a dependency from Phase 50. No new Python or system dependencies are introduced. (If V6 finding diverges — e.g., slowapi not present — CC halts per §22 below.)
Alembic head expected unchanged at 0064. The typed-MemoryObject-without-migration pattern (Phase 50/52/53 precedent) governs Sub-arc 1c's UploadEventReceived class addition. If the pattern fails to compose (e.g., the event class's storage shape requires a column-level change), CC halts per §22 — scoping v0.3 would absorb and Alembic head would advance from 0064 to 0065.
Phase 59's engine-side work decomposes into three sub-arcs across four build segments. The decomposition mirrors the scoping note's Stage 2 per-item characterization tables, refined here for build sequencing.
Sub-arc (i) — Wave 7 engine substrate. Components 1a through 1e. Five wiring/persistence items the Phase 58 close handoff identified as deferred Wave 7 work. Lands in Build Step 1.
Sub-arc (ii) wiring half — API-key resolution + narration + confidence + prompt-for-process. Components 2a through 2f. The wiring side of v1-image-and-audio capability reaching first-quality through the new pathway: per-skill API-key resolution; narration shaping; classifier confidence model; multi-valid-prompt-for-process detection [NEW v0.2]. Mechanical wiring on top of Sub-arc (i)'s credential-store consolidation. Lands in Build Step 2.
Sub-arc 2f composes with 2e (both ship engine-side data-shape additions consumed by Phase 60 OL — AskOperatorToClarify and AskOperatorForProcess respectively) and with 2d (2d's accompanying-message input can pre-empt 2f's elicitation when the message disambiguates purpose; see §13 composition paragraph). 2e and 2f are orthogonal surfaces — engine certainty (2e) and Operator purpose (2f) — not alternatives.
Sub-arc (ii) quality half — OCR tuning + reliability surface. Components 3a through 3c. The Operator-judgment-shaped side: OCR threshold model refinement; OCR class-aware parameter refinement; content-reliability warning surface. First-pass tuning where calibration-shape findings are expected to surface. Lands in Build Step 3.
Sub-arc (iii) engine half — Fallback affordance endpoint. Component 4a. The engine half of the v1-unsupported-format pathway: the CR §7.4 fallback affordance endpoint Phase 58 deferred. Lands in Build Step 4.
The two intra-phase Operator checkpoints — Checkpoint A after Build Step 1 (Operator confirms Wave 7 wiring before Sub-arc (ii) wiring half begins) and Checkpoint B after Build Step 3 (Operator confirms quality work before Sub-arc (iii) begins) — separate mechanical wiring from Operator-judgment-shaped work, and quality tuning from the engine-half close. Checkpoint C is the final pre-tag checkpoint at Build Step 5.
Sub-arcs flow strictly in order: (i) → (ii) wiring → (ii) quality → (iii) engine half. The credential-store consolidation in 1a is a prerequisite for 2a/2b/2c (which read from it); the UploadEventReceived event in 1c is independent of subsequent sub-arcs but lands in Sub-arc (i) because it's substrate-shaped persistence work. The narration multi-pathway restore in 2d is independent of the credential-store work but groups with the wiring half because the engine-side classifier confidence model in 2e and the multi-valid-prompt-for-process detection in 2f are the cross-cutting wiring on top of which 3a–3c's quality tuning runs.
Documentation of the multi-pattern credential resolution shape that emerged from Phase 16 (system-level via app.state.openai_api_key) coexisting with Phase 58's credential-store abstraction (engagement-level resolution). Plus new-pathway transformation skills wired to read from the credential-store.
The framing per scoping v0.1 §Stage 2.1: the two patterns serve genuinely different scopes. Phase 16's voice-mode endpoint operates at the system level — one OpenAI key for the deployment's transcription work. Phase 58's credential-store operates at the engagement level — per-engagement keys keyed by credential type. Both are legitimate; neither retires the other.
The substrate-level work this sub-arc covers. Three things:
docs/credential-resolution-v0_1.md (or analogous; CC selects placement per Phase 58 docs conventions; CRV-2 will surface the credential-store interface that anchors this) that explicitly records the two scopes and the resolution chain for new-pathway skills. The document is the source-of-truth for future-phase decisions on whether to retire one of the patterns or keep both indefinitely.transcribe_audio_skill or its app.state.openai_api_key reads. The multi-pattern coexistence is the point — Phase 59 does not migrate the Phase 16 surface to the credential-store.app.state pattern still live for voice-mode endpoint. The HOLDS verdict means Phase 59 wires new-pathway skills to credential-store; multi-pattern accepted.
CC reads the credential-store class definition and key-resolution method signature at execution time, verifying the call-site shape Sub-arc 2a/2b/2c's wiring lands on. Naming-only divergence (e.g., method named get_key instead of resolve) absorbs in-flight; architectural divergence (e.g., method signature requires fundamentally different arguments) halts.
docs/credential-resolution-v0_1.md exists, documents the two scopes (system-level vs. engagement-level), names the resolution chain for new-pathway skills (engagement-level credential first; system-level fallback if applicable), names the deliberate preservation of Phase 16's app.state pattern for the voice-mode endpoint.
The put_blob method exists on the ObjectStore class (per Step 0 V3 findings — Phase 58 shipped the abstraction; the method exists but isn't called end-to-end from the production upload endpoint flow). Sub-arc 1b wires put_blob into the upload endpoint's persistence path.
The upload endpoint at src/loomworks/api/routers/files.py (per Phase 53 V6 / Phase 59 Step 0 V7 evidence) currently reads the file body into memory and passes it forward. Sub-arc 1b replaces the in-memory persistence with an ObjectStore put_blob call that writes the file body to the underlying blob storage (MinIO local; S3-compatible production per Phase 58's ObjectStore abstraction).
The call-site shape per CRV-1 (read at execution time): the put_blob method signature determines the call. CC reads it verbatim from the ObjectStore class definition, identifies the bytes-input and metadata-input arguments, and wires the call into the upload endpoint flow at the appropriate point (after content-type detection has identified the artifact; before upload_event_received event persistence per Sub-arc 1c).
Sub-arc 1e (streaming-upload optimization) replaces whole-file read with chunked streaming. The streaming pattern must compose with put_blob: the streaming reader feeds chunks to put_blob rather than buffering the whole file in memory then calling put_blob once. The exact composition depends on put_blob's signature — whether it accepts a stream/generator or a bytes buffer. CC reads CRV-1 at execution time and decides:
put_blob accepts streaming input. 1e composes naturally; the streaming reader pipes directly to put_blob.put_blob accepts only bytes. 1e and 1b need an ordering decision: either 1b lands first (with whole-file read still in place) and 1e refactors both 1b's call and the upstream read; or 1e lands first (introducing the streaming reader) and 1b's wiring takes the streaming-shape input. Either ordering works; CC selects the simpler refactor path.put_blob's shape forces an ordering constraint the scoping note didn't anticipate (e.g., put_blob requires a content-length header that streaming can't provide until the read completes), CC halts per §22 — Sub-arc (i) ordering decision becomes scoping-shaped.put_blob method exists; no call-sites in the upload endpoint flow (Wave 7 deferral as scoping assumed). HOLDS verdict means Sub-arc 1b proceeds as specified.put_blob called end-to-end from the upload endpoint flow. The blob persists to the ObjectStore-managed storage; a blob reference (URI, ID, or whatever the method returns) is captured for inclusion in the UploadEventReceived event (Sub-arc 1c).
A new typed MemoryObject subclass upload_event_received capturing the "an upload happened" Memory event Phase 58 deferred persisting. Schema, _ANCHOR_PRIORITY entry, no Alembic migration per the typed-MemoryObject-without-migration pattern (Phase 50/52/53 precedent inherited per P59-D7).
The UploadEventReceived class lives alongside recent typed MemoryObject classes per the Phase 50/52/53 placement convention (CC reads CRV-4 at execution time to identify the convention's current shape; locates the recent additions and matches their pattern). Schema fields:
object_type: Literal["upload_event_received"] — required discriminator per the typed-MemoryObject pattern.file_id: UUID — the file identifier the upload endpoint assigned.engagement_id: UUID — the engagement scope.uploaded_by: ActorRef — the Operator (or system-actor sentinel for any non-Operator-initiated path) who uploaded.original_filename: str — the filename as received from the upload client.content_type: str — the content type detected by the upload pipeline.file_size_bytes: int — the size in bytes; bounded by the 50 MB Phase 16 limit.uploaded_at: datetime — the timestamp of upload acceptance.blob_reference: str — the reference the ObjectStore put_blob returned (URI, ID, or whatever shape Sub-arc 1b produces)._ANCHOR_PRIORITY entry
CC adds "upload_event_received": "standard" to the _ANCHOR_PRIORITY dict per CRV-3 (read at execution time per Phase 53 V7 evidence at events.py:112–130; placement convention follows whatever ordering the existing entries use — alphabetical, insertion-order, or grouped-by-domain). The "standard" priority value mirrors Phase 50/52/53 typed-MemoryObject additions.
OBJECT_TYPE_REGISTRY deferred
Per P59-D7, no OBJECT_TYPE_REGISTRY entry added in Phase 59. The Phase 50/52/53 precedent: OBJECT_TYPE_REGISTRY entries are added when downstream consumers exist; Phase 59 doesn't ship a downstream consumer (Phase 60's OL surfaces will be the first consumer; a future phase adds the registry entry when the consumer integration lands). The deferral preserves the typed-MemoryObject-without-migration pattern.
The typed-MemoryObject pattern stores discriminated objects in the existing Memory event table's content column (per Phase 50/52/53 implementation). No schema change required. Alembic head stays at 0064.
Halt condition. If the event class's storage requirements force a schema change (e.g., a field type not representable in the existing content column), CC halts per §22. The pattern fails to compose; scoping v0.2 absorbs and Alembic head advances.
_ANCHOR_PRIORITY dict accepts new entries; typed-MemoryObject-without-migration pattern still convention. HOLDS verdict means Sub-arc 1c proceeds as specified.UploadEventReceived class exists at the conventionally appropriate location._ANCHOR_PRIORITY entry present and produces standard-priority anchoring at event-write time.upload_event_received event after put_blob succeeds, with all schema fields populated correctly.
slowapi rate-limit decorator on the upload endpoint at src/loomworks/api/routers/files.py. Per P59-D8: slowapi is the library (Step 0 V6 confirms slowapi is in pyproject.toml and integrated on Phase 50's public credit-request endpoint). Per the Phase 50 pattern, CC adds a @limiter.limit(...) decorator on the upload endpoint.
CC reads CRV-5 at execution time: the verbatim @limiter.limit(...) decoration on Phase 50's public credit-request endpoint. The upload endpoint's rate value is set lower per-minute than the public credit-request endpoint's rate, because per-call cost on upload is higher (file persistence + skill dispatch + Memory event write). The exact value lands at CR drafting time as a placeholder pending CRV-5 confirmation — likely something on the order of 5–10 uploads per minute per engagement, with a daily ceiling for envelope-protection. CC selects a defensible value during execution; the value documents in implementation notes for future-phase calibration.
slowapi available; integrated on Phase 50's public form endpoint; not yet on upload endpoint. HOLDS verdict means Sub-arc 1d proceeds as specified.@limiter.limit(...) decorator wired against the slowapi Limiter instance.phase-59-implementation-notes-v0_1.md per §25).RateLimitExceeded exception path exercised).
The upload endpoint's whole-file read pattern (per Step 0 V7 findings — await file.read() or equivalent that buffers the whole upload in memory) replaced with streaming. The streaming pattern reads the upload body in chunks; each chunk feeds forward to put_blob (Sub-arc 1b composition).
CC reads CRV-6 at execution time: existing streaming patterns in the codebase (other endpoints, ObjectStore implementations) for the chunking convention. The chunk size matches the codebase convention or, if no convention exists, lands at a default like 1 MB chunks (large enough to avoid syscall overhead; small enough to keep memory usage bounded).
The 50 MB size limit from Phase 16 still holds under streaming. Implementation: a running byte counter incremented on each chunk read; when the counter exceeds the limit, the upload aborts with the appropriate error response. The check fires per-chunk, not at completion — the limit cannot be bypassed by sending an oversized upload incrementally.
See §8.3 for the 1b/1e composition discussion. The decision lands at execution time based on put_blob's signature (CRV-1).
The three new-pathway transformation skills — audio_transcription, image_vision_analysis, image_ocr — wired to read API keys from the credential-store (Sub-arc 1a establishes the credential-store interface and resolution helper that these sub-arcs consume).
audio_transcription
The new-pathway audio skill (introduced at Phase 58 for the upload pathway; distinct from Phase 16's voice-mode transcribe_audio_skill) reads its OpenAI API key from the credential-store. Engagement-level resolution: if the engagement has a configured OpenAI key, use it. Fallback: system-level key via app.state.openai_api_key if no engagement-level key is configured. The fallback is the documented multi-pattern resolution chain per P59-D2.
image_vision_analysisThe new-pathway vision skill reads its Anthropic API key (Claude-with-vision is the model family; separate key family from OpenAI) from the credential-store. Engagement-level resolution: if the engagement has a configured Anthropic key, use it. Fallback: system-level if configured. If no system-level Anthropic key exists either, the skill fails with a credential-resolution error that surfaces meaningfully to the Operator (per the only-show-what-is-available principle — the failure must be honest).
image_ocrThe new-pathway OCR skill's API-key handling depends on the OCR backend Phase 58 chose. CC reads at execution time:
CC determines the OCR backend by reading the image_ocr skill module at execution time; the existing implementation will reveal whether external service is involved.
audio_transcription, image_vision_analysis, image_ocr) are confirmed live. CC consults V1's evidence for exact skill file paths.The Companion narration of uploaded files restored to multi-pathway behavior. Phase 58 shipped narration in content-isolation mode (extraction result only). Sub-arc 2d restores the multi-pathway: narration consumes both the extraction result AND any accompanying Operator message that arrived with the upload. Empty accompanying-message path falls back to the current isolation behavior — multi-pathway is additive; the existing single-input shape is preserved as the empty-message degenerate case.
Drift verdict (restore multi-pathway). Phase 58 collapsed narration to single-input because the upload pathway didn't yet thread accompanying messages through to the narration composer. The collapse was drift, not deliberate; restoring the multi-pathway recovers the design intent that the Companion's narration of an upload engages with the Operator's context for the upload, not just the artifact in isolation.
Per CRV-8 (read at execution time): the narration composition function lives engine-side (Step 0 V9 confirms HOLDS — narration takes extraction result only; no accompanying-message input). CC reads the composition function signature and identifies the addition point for the accompanying-message input.
The prompt template revision lives at src/loomworks/prompts/ (Phase 53 precedent for prompt-template placement; CC confirms exact path at execution per CRV-8). The revision adds an optional accompanying_message field to the template's input schema. When present, the message shapes the narration's focus — the Companion responds to both the artifact and the Operator's framing of why they uploaded it. When absent, the narration runs in the current single-input shape.
A halt condition surfaces if the narration's output shape (the data the OL converse view consumes) needs to change to accommodate multi-pathway. The scoping note's envelope premise is that Phase 59 ships engine-only — any OL-side shape change defeats the envelope. If the output shape change is unavoidable, CC halts per §22; scoping v0.2 (or v0.3) absorbs the wider surface or splits Sub-arc (ii) across phases.
The expected case: the output shape stays the same. The narration prompt template changes its input; the output's narration text remains the same kind of text. OL converse view reads whatever the narration produces without caring whether the narration came from one-input or two-input composition.
accompanying_message input.If the upload arrives with an accompanying message that disambiguates purpose between multiple candidate skills (e.g., "transcribe this contract" → OCR; "what's on this whiteboard" → vision-analysis), the engine consults the message at detection time before checking 2f's multi-valid-candidates condition. When the message disambiguates, dispatch proceeds to the matching skill and 2f does not fire. When the message does not disambiguate (empty message; off-topic message; or message that doesn't clarify purpose), 2f fires per its standard flow (§15).
Implementation: a disambiguation-check helper between detection and dispatch; the helper consults the accompanying message via the existing 2d input pathway. The check shape — keyword matching, schema check, or LLM call — lands at execution time per the existing 2d composition function's posture; CC selects the lower-friction path consistent with 2d's accompanying-message consumption (§13.3). If the check requires an LLM call and the LLM-call posture wasn't decided at scoping, CC halts per §22 — re-draft handoff §7 names this as a halt-and-escalate signal.
Tests cover both the pre-emption case (accompanying message disambiguates; dispatch proceeds; 2f does not fire) and the message-doesn't-disambiguate case (accompanying message present but doesn't clarify purpose; 2f fires per standard flow). Counted in §15 acceptance, not §13's, to keep the 2f surface co-located.
The detection registry's dispatch (per Step 0 V8 — single-skill commit today; no confidence output) extended with a confidence model. The dispatch returns a confidence score alongside the selected skill. Confidence below a threshold triggers a re-dispatch path that surfaces an "ask Operator to clarify" affordance.
What 2e covers — and what it doesn't. 2e covers confidence-disambiguation: engine isn't sure it dispatched correctly. The shape it produces (AskOperatorToClarify) carries an accuracy-question: "I dispatched this to skill X with confidence Y; was that right?" The orthogonal surface — purpose-disambiguation, multiple legitimate skills firing at high confidence where the choice is the Operator's purpose, not engine accuracy — is covered by Sub-arc 2f (§15). See §15.2 for the framing distinction and §6 sub-arc decomposition for the 2e/2f composition.
Phase 59 ships the engine-side data + the contract for OL consumption. Phase 60 carries the OL surface that consumes the ask-Operator-to-clarify data.
Drift-justified verdict. Single-skill-commit dispatch is accepted as deliberate — the engine commits to one transformation skill per upload, rather than running multiple skills and comparing outputs (which would multiply API costs and complicate Memory event semantics). The confidence model is additive: it adds threshold-driven re-dispatch (ask the Operator if uncertain), not multi-skill comparison.
The distinction matters. Multi-skill comparison was a candidate path the scoping conversation considered and rejected: it would treat low-confidence dispatch as a resource-allocation problem (try multiple skills; compare) rather than as an Operator-engagement problem (the engine doesn't know; ask). The Operator-engagement framing matches the only-show-what-is-available principle and the plain-English-communication-with-Operators principle — when the engine is uncertain, it says so and asks, rather than papering over uncertainty with comparison heuristics.
v0.2 sharpening on the framing. Scoping v0.1 compressed two distinct Operator-engagement-at-dispatch surfaces — confidence-disambiguation and purpose-disambiguation — into the single classifier confidence model. Operator review during CR drafting (Move 5) caught the compression. The two surfaces are genuinely distinct: engine certainty (am I dispatching correctly?) and Operator purpose (which legitimate skill do you want?). The engine can be very certain about classification while being entirely uncertain about purpose, and vice versa. v0.2 §Stage 2.2 separates them; this CR's §14 covers 2e (confidence-disambiguation only); §15 covers 2f (purpose-disambiguation). The compression v0.2 caught is second-order evidence for the v1-multi-pathway-collapses-inquiry-as-protocol candidate — the inquiry applied at scoping time can produce its own compressions (Operator review is the recovery mechanism). See §24.2 methodology candidates carry-forward.
Per CRV-7 (read at execution time): the detection registry's dispatch method signature. Sub-arc 2e extends the signature to return (SkillReference, confidence: float) rather than just SkillReference. The confidence score's range is 0.0 to 1.0; values close to 1.0 indicate high confidence in the dispatch (e.g., a .docx file matched to docx_extraction via MIME type — strong signal); values close to 0.5 or lower indicate uncertainty (e.g., an unknown content type matched only by a fallback heuristic).
The threshold value: CC selects at execution per defensible-default heuristics. A reasonable starting threshold is 0.7 — above it, dispatch proceeds; below it, the re-dispatch path engages. The threshold value documents in implementation notes for future-phase calibration.
The re-dispatch path: when confidence is below threshold, the dispatch returns an AskOperatorToClarify shape (a typed object describing the candidate skills and their confidence scores). The upload endpoint catches this shape and returns it in its response. Phase 60's OL surface consumes the response and renders the clarification affordance.
Composition with 2f (§15) at the substrate level. Per §15.3: 2f extends the same dispatch to support "return all candidates above threshold" semantics for the multi-valid case. The dispatch evolves to support both 2e's "best candidate with confidence" semantics and 2f's "all candidates above threshold" semantics — either one fires per upload, depending on the candidate landscape. CC implements both extensions in Sub-arc 2 build sequence (Step 2); ordering within the step lands at execution time. If the two semantics can't compose cleanly in the dispatch surface — e.g., the schema can't represent both result shapes — CC halts per §22.2.
The OL contract per the envelope premise: Phase 59 ships the engine-side data shape; Phase 60 builds the OL surface that consumes it. CC sketches the expected OL consumer shape during CR execution and documents it in implementation notes for Phase 60's scoping. A halt condition fires (per §22) if the contract reveals OL-side constraints Phase 59 didn't anticipate — e.g., the OL surface needs more or different data than the engine's shape provides. In that case, scoping v0.3 or cross-phase scoping reshapes.
Phase 60 OL consumer of AskOperatorToClarify is anticipated to be similar to but distinct from the Phase 60 OL consumer of AskOperatorForProcess (§15.5). Different elicitation framings: accuracy-question (2e) vs. purpose-question (2f). Both consume from the same upload endpoint response slot — the shape returned alongside the upload acknowledgment.
(skill, confidence) rather than just skill.AskOperatorToClarify shape exists; upload endpoint returns it in its response when the threshold triggers.AskOperatorToClarify shape returned); boundary case (exactly at threshold).
Engine-side detection-time check: when the detection registry surfaces multiple registered skills matching the upload's content type at confidence above the P59-D4 threshold, the engine produces an AskOperatorForProcess shape rather than committing to a single skill. The shape carries per-candidate purpose-framing strings the Companion uses to compose an elicitation prompt at the OL surface (Phase 60 renders).
Canonical example. An image upload triggers both image_vision_analysis (treat as photograph; describe what's in it) and image_ocr (treat as scanned document; extract the text). Each fires at high confidence on its own — the engine isn't uncertain in the classification-accuracy sense (the 2e surface). The choice between them is the Operator's purpose: "transcribe text" or "describe content"? The engine asks; the Operator answers; dispatch proceeds.
Shape name resolution. AskOperatorForProcess — the existing AskOperatorToClarify shape (P59-D4 surface) establishes the "Ask Operator [preposition] [thing]" family in the codebase; AskOperatorForProcess extends that family while preserving the Operator's "prompt for process" phrasing. The name is settled at this re-draft per P59-D11 §3 resolution; final substrate naming lands per CRV-7 (read at execution time). Naming-only divergence from the codebase family absorbs in-flight; architectural divergence (e.g., the codebase favors Disambiguation or Elicitation prefixes for this shape family) halts per §22 — re-draft handoff §7 names this as a halt-and-escalate signal.
Phase 59 ships the engine-side data shape + the contract for OL consumption. Phase 60 carries the OL surface that consumes the prompt-for-process data with the per-candidate purpose-framing strings.
Drift verdict, restore multi-pathway. Scoping v0.1 compressed confidence-disambiguation and purpose-disambiguation into a single classifier confidence model (P59-D4). The compression had two reinforcing causes (per scoping v0.2 §Move 5): the upload-pathway investigation that seeded Sub-arc (ii) framing named "when detection is ambiguous, the engine asks a purpose-shaped question" but didn't separate purpose-disambiguation from confidence-disambiguation; and scoping v0.1 was running the v1-multi-pathway-collapses inquiry on Phase 58's outputs and naturally surfaced the single-skill-commit vs. multi-skill comparison axis (which P59-D4 settled as accept-single-skill-commit). The two-distinct-surfaces axis was a different question — about what the Operator engages on, not about how the engine dispatches — and got compressed under the dispatch-posture decision.
v0.2 separates them. The two surfaces are orthogonal: engine certainty (am I dispatching correctly? — covered by 2e) and Operator purpose (which legitimate skill do you want? — covered by 2f). The engine can be very certain about classification while being entirely uncertain about purpose, and vice versa. Compressing them treats Operator-engagement-at-dispatch as a one-dimensional problem (engine certainty); the two-surface shape recognizes that engine certainty and Operator purpose are orthogonal concerns.
The two surfaces compose with Sub-arc 2d (§13.7): if the Operator provided an accompanying message that already disambiguates purpose ("scan the contract" or "what's on the whiteboard"), 2f's elicitation does not fire — the message answers the question pre-emptively. If no accompanying message OR the message doesn't disambiguate, 2f fires per its standard flow.
Per CRV-7 (read at execution time): the detection registry's dispatch surface. 2f extends the dispatch to support "return all candidates above threshold" semantics alongside the existing "return best candidate" semantics that 2e (§14.3) provides. The two extensions compose in the same dispatch method — either result shape fires per upload depending on the candidate landscape:
AskOperatorToClarify returned.AskOperatorForProcess returned (modulo §13.7's 2d pre-emption).
The per-candidate purpose-framing strings live at the skill-registration layer (each transformation skill declares its purpose-framing at registration: e.g., image_vision_analysis declares "describe what's in the image"; image_ocr declares "transcribe the text in the image"). The declaration shape: CC selects at execution time per the existing skill-registration interface; the framing is a single short string per skill, surfaced through the registry-query path. If the existing skill-registration interface doesn't accept declarative metadata of this shape — e.g., it's a simple register(skill_name, skill_class) call with no metadata slot — CC halts per §22; re-draft handoff §7 names this as a halt-and-escalate signal.
The detection-time check fires after the dispatch returns; if multiple candidates are present above threshold AND Sub-arc 2d's accompanying-message disambiguation pre-emption check (§13.7) does not fire, the engine produces the AskOperatorForProcess shape. The shape's payload: an array of {skill_reference, purpose_framing_string, confidence} tuples — one per above-threshold candidate. The upload endpoint catches the shape and returns it in its response slot (the same slot 2e's AskOperatorToClarify uses; only one of the two shapes fires per upload).
Phase 60 OL consumer of AskOperatorForProcess is anticipated to be similar to but distinct from the AskOperatorToClarify consumer (§14.4). Both consume from the same upload endpoint response slot. The elicitation framing differs: AskOperatorForProcess frames as purpose-question ("you uploaded a file that could be processed two ways — which do you want?"); AskOperatorToClarify frames as accuracy-question ("I think this is X with confidence Y — is that right?"). The OL surface chrome may share components but the elicitation copy differs.
CR re-drafter sketches the expected Phase 60 consumer shape during CR execution; mid-Phase-59 surface that contradicts the sketch warrants halt per §22. The sketch lands in implementation notes for Phase 60's scoping input.
A halt condition fires (per §22.2) if AskOperatorForProcess and AskOperatorToClarify can't compose cleanly with the upload endpoint's response schema — e.g., both need to fire on the same response and the schema can't represent the union (this shouldn't happen because the candidate landscape is mutually exclusive, but the schema slot still needs to represent either shape). The expected case: the response slot is a discriminated union; either shape lands in it; neither fires when standard single-skill dispatch proceeds.
AskOperatorForProcess shape when multi-valid AND no accompanying-message-disambiguation pre-emption (per §13.7).Refinement of the OCR's confidence-output-to-warning-decision mapping. The first-pass shipped at Phase 58 — OCR returns some confidence score; some mapping from confidence to "warn the Operator" decision exists. Phase 59 calibrates the mapping for first-quality.
CC reads the existing OCR threshold logic at execution time (the image_ocr skill module surface — file paths per Step 0 V1 evidence). The refinement specifies threshold values and the test fixtures that exercise them:
unreliable_succeeded reliability state (consumed by Sub-arc 3c).unreliable_succeeded with strong warning, or failed if confidence is below the failure threshold. The calibration is what Phase 59 settles.The threshold values document in implementation notes. The exact numbers depend on the OCR backend's confidence-output range (CC reads the backend's documentation at execution time).
Per scoping v0.1 §Stage 4.2 reserved-slot allocation, first-pass tuning is inherently surfacing. Phase 59 is the first phase where OCR threshold-model tuning runs against real image content; the calibration will reveal whether the scoping note's threshold-and-warning framing fits cleanly or surfaces new shape (e.g., per-character thresholding vs. document-level; per-image-class threshold variation per Sub-arc 3b).
The reserved-slot allocation explicitly anticipates this. If a calibration finding requires CR amendment scoping (e.g., the OCR backend doesn't surface the confidence output Phase 59 needs in a useful form), the mid-build amendment slot for Sub-arc (ii) quality engages per §22.
V10 (content-reliability surface absence — Sub-arc 3c-relevant) and V8 (confidence model — Sub-arc 2e-relevant) together inform Sub-arc 3a indirectly. The OCR-specific evidence comes from CC reading the image_ocr skill module at execution time; Step 0 didn't enumerate the existing OCR threshold logic in detail.
OCR parameter sets vary per image class (camera-captured vs. clean digital scan vs. low-quality scan). The classification logic — which image class an upload belongs to — is part of the OCR skill module (or a pre-step in the detection registry; CC selects at execution time per the existing module shape).
The image-class classifier looks at image properties (resolution, aspect ratio, color histogram, edge density, or similar heuristics; the exact signal set lands during construction) and identifies the class. The OCR engine then runs with the parameter set appropriate to the class:
The classifier's output also feeds the OCR threshold model in Sub-arc 3a — class-specific thresholds may differ.
Same reserved-slot allocation as 3a. First-pass tuning surfacing; calibration shape may diverge from scoping note expectations. Reserved slot engages if amendment is needed.
The ExtractionResult dataclass per CRV-9 (read at execution time per Step 0 V10 evidence) gains a reliability field with three values: succeeded, unreliable_succeeded, failed. The third state — unreliable_succeeded — surfaces a warning alongside the assertion content; Phase 60's OL surface displays the warning; Phase 59 ships the data shape and the engine-side classification of which extraction outcomes land in which state.
Critically: assertion content persists in all three success cases (including unreliable_succeeded). The warning is metadata, not a rejection. The Operator sees the assertion (content extracted by OCR or vision) AND the warning ("this extraction may not be reliable; review carefully"). The Operator can accept, edit, or reject the assertion; the engine does not pre-empt the decision.
Drift verdict (restore multi-pathway). Phase 58 collapsed extraction outcomes to a two-state shape (succeeded / failed). The collapse was drift; the three-state surface restores the design intent that some extractions succeed-but-with-caveats — the assertion is good enough to surface but should carry an honest warning rather than be presented as if fully reliable.
This connects to the only-show-what-is-available and plain-English-communication-with-Operators principles. A two-state surface forces the engine to choose: either present unreliable extractions as fully reliable (dishonest) or reject them entirely (loses useful content). The three-state surface honors both — surface the content, name the unreliability.
Per CRV-9: read the ExtractionResult dataclass; add the reliability field as a three-value enum. The enum members are SUCCEEDED, UNRELIABLE_SUCCEEDED, FAILED (or whatever naming convention the codebase favors; CC matches existing enum conventions at execution time).
The classification logic — which extraction outcomes produce which reliability state — depends on the skill family. For OCR (Sub-arcs 3a/3b), the threshold mapping produces the state. For vision (image_vision_analysis), the confidence output from the Claude-with-vision API call produces the state (the API surfaces a confidence indicator on the vision response; CC verifies the surface at execution). For audio (audio_transcription), Whisper produces per-segment confidence; aggregate confidence below threshold maps to unreliable_succeeded.
The OL response shape carries the reliability state through. Phase 60 will read the field and render the warning surface.
ExtractionResult dataclass has a reliability field with three-value enum.unreliable_succeeded assertions persist in Memory with the same content as succeeded assertions, plus the reliability metadata.
A new engine endpoint per Phase 58 CR §7.4: the manual-content-contribution fallback affordance for detection-failed cases. Operator-facing path: file uploaded successfully (200 from upload endpoint); detection-failed (no skill matched, or all matched skills returned FAILED reliability); Phase 60's OL surface lets Operator type content manually; the manual content posts to this fallback endpoint, which produces a Memory event and assertion the same way successful extraction would.
The candidate-landscape four-way distinction (per §15.4). Sub-arc 4a covers the zero-candidate case of the candidate-landscape space; 2e covers the one-candidate-low-confidence case; standard dispatch covers the one-candidate-high-confidence case; 2f covers the multi-candidate-above-threshold case. The four cases together exhaust the candidate-landscape space; 4a is the orthogonal complement to 2e and 2f, not an alternative to them. The 2d accompanying-message pre-emption (§13.7) sits one level up — it can convert a 2f case to a standard-dispatch case before 2f fires, but it does not interact with the 4a zero-candidate case (no candidate means there's nothing to disambiguate).
CC reads CRV-10 at execution time: existing endpoints in src/loomworks/api/routers/files.py per Step 0 V11 evidence. CC selects placement for the new endpoint following the convention (engagement-scoped per the universal convention from Phase 53 V10 / scoping v0.1 §Stage 2.3).
Proposed endpoint shape (CC confirms or adjusts at execution time):
POST /engagements/{engagement_id}/files/{file_id}/manual-content
Request body:
{
"content": "<the Operator's typed text content>",
"content_type": "text/markdown" | "text/plain"
}
Response: the Memory event reference + assertion reference produced by the manual contribution.
The endpoint behaves as a fallback for the upload_event_received event already persisted (Sub-arc 1c) — it doesn't create a new upload event; it adds a manual-contribution assertion attached to the existing file_id.
CC determines at execution time whether the fallback endpoint produces:
manual_content_contributed Memory event class (analogous to upload_event_received but for manual content), orEither approach is acceptable; the choice depends on whether existing event classes fit naturally or would require force-fitting. CC selects the lower-friction option and documents the choice in implementation notes.
Engagement-scoped per the universal convention. The endpoint requires the same authentication the upload endpoint requires; the Operator is the actor on the resulting Memory event.
A halt condition fires (per §22) if the endpoint contract reveals Phase 60-side constraints the scoping note didn't anticipate. CC sketches Phase 60's consumer shape during CR execution; mid-Phase-59 surface that contradicts the sketch warrants halt and cross-phase scoping.
file_id.Per CR drafting handoff §7 and scoping v0.2 §Test surface estimate, total new engine tests in the range ~26–41 always-run (v0.2 widens from v0.1's ~23–37 by +3–4 for Sub-arc 2f tests):
| Sub-arc | Component | Estimate | |---------|-----------|----------| | (i) | 1a credential-store consolidation | 1–2 | | (i) | 1b ObjectStore put_blob end-to-end | 2–3 | | (i) | 1c UploadEventReceived event class | 3–4 | | (i) | 1d rate-limit middleware | 2 | | (i) | 1e streaming-upload | 2–3 | | (ii) wiring | 2a/2b/2c API-key resolution | 3–6 | | (ii) wiring | 2d narration multi-pathway | 3 | | (ii) wiring | 2e classifier confidence model (confidence-disambiguation) | 3 | | (ii) wiring | 2f multi-valid-prompt-for-process (purpose-disambiguation) [NEW v0.2] | 3–5 | | (ii) quality | 3a OCR threshold model | 2–3 | | (ii) quality | 3b OCR class-aware parameters | 2–3 | | (ii) quality | 3c content-reliability warning surface | 3–4 | | (iii) engine | 4a fallback affordance endpoint | 3–5 |
Estimate range: 32–46 tests across the sub-arcs; the lower bound of the scoping note v0.2's 26–41 reflects natural absorption where components share test infrastructure (e.g., 2a/2b/2c may share fixtures; 2e/2f may share dispatch-surface fixtures; 3a/3b share the OCR fixture pipeline). The upper bound assumes minimal sharing.
Post-Phase-59 target: existing baseline (~2,146 always-run + 26 skipped per Phase 58 close) + ~26–41 new = ~2,172–2,187 always-run + 26+ skipped. (v0.2 widens from v0.1's ~2,169–2,183 by +3–4.)
Tests group per Build Step (§21):
phase-58-implementation-notes-v0_2.md §11.1 inventory). API-key resolution tests verify resolution; they don't make live API calls.AskOperatorToClarify), multi-candidate-above-threshold (2f fires AskOperatorForProcess), zero-candidate (4a fallback path). The 2d pre-emption case (accompanying message disambiguates a multi-candidate situation) exercises in 2f's positive tests per §15.7.Zero new OL vitest in Phase 59. Phase 60 carries OL-side tests. The existing 167 OL vitest stay green throughout Phase 59's life.
Likely zero per typed-MemoryObject-without-migration pattern (P59-D7). CC confirms at execution; if migration becomes necessary, CC halts per §22 (the pattern fails to compose) and Alembic head advances from 0064 to 0065. The likelihood is low given Phase 50/52/53 precedent.
Per §22, test-count divergence from estimate greater than 50% (i.e., >62 new tests across the phase, v0.2 widening from v0.1's >55) halts. The estimate's 50% buffer accommodates routine surfacing during build; beyond 50% the phase has changed shape and scoping should re-engage.
5 active steps + 4 reserved buffer slots = 9 slots total. Reserved-slot-as-halt-condition-pre-commitment per the methodology candidate from Phase 50 onward, refined at Phase 58 to allow envelope-breach with explicit Operator authorization.
Step 0 — Pre-flight + CR archival + build branch creation. CC archives this CR to engine repo docs/phase-crs/phase-59-cr-upload-pathway-completion-v0_2.md (preserves phase-59-cr-upload-pathway-completion-v0_1.md alongside as superseded per re-draft handoff §6 preservation principle). Runs the pre-flight reads per §4 (baseline tag check; test-count baseline; Alembic head; CR-registry confirmation). Creates branch phase-59-upload-pathway-completion from main on engine repo. (No OL branch — Phase 59 is engine-only per scoping v0.2.) Estimated wall-time: ~10 min.
Step 1 — Sub-arc (i) Wave 7 engine substrate. Components 1a through 1e in order: credential-store consolidation documentation (1a) → ObjectStore put_blob wiring (1b) → UploadEventReceived Memory event class + _ANCHOR_PRIORITY entry (1c) → rate-limit middleware (1d) → streaming-upload optimization (1e). Tests: ~10–14 new. Step 1 closes when all five components' acceptance criteria green per §7–§11.
Checkpoint A — Operator confirmation after Step 1. CC pauses; produces a step-summary; Operator confirms Wave 7 wiring on test cases before Step 2 (Sub-arc (ii) wiring half) begins. The checkpoint separates mechanical substrate work (Sub-arc (i)) from the wiring half (Sub-arc (ii)) that integrates with Phase 60's anticipated OL consumer shapes. v0.2: confirmation scope widens to include both 2e (confidence-disambiguation) and 2f (purpose-disambiguation) surfaces — Operator confirms both engine-side data shapes before Step 2's six components begin.
Step 2 — Sub-arc (ii) wiring half. Six components: 2a/2b/2c (per-skill API-key resolution; audio, vision, OCR), 2d (narration multi-pathway restore), 2e (classifier confidence model — confidence-disambiguation), 2f (multi-valid-prompt-for-process — purpose-disambiguation) [NEW v0.2]. Tests: ~12–17 new (v0.2 widens from v0.1's ~9–12 by +3–5 for 2f). Step 2 closes when components' acceptance criteria green per §12–§15.
Step 3 — Sub-arc (ii) quality half. Components 3a (OCR threshold model refinement), 3b (OCR class-aware parameter refinement), 3c (content-reliability warning surface). Tests: ~7–10 new. Step 3 closes when components' acceptance criteria green per §16–§18.
Checkpoint B — Operator confirmation after Step 3. CC pauses; produces a step-summary including any calibration-shape findings from 3a/3b tuning; Operator confirms quality work before Step 4 (Sub-arc (iii) engine half) begins. The checkpoint separates quality tuning (where first-pass surfacing is expected) from the engine-half close.
Step 4 — Sub-arc (iii) engine half. Component 4a (fallback affordance endpoint). Tests: ~3–5 new. Step 4 closes when 4a's acceptance criteria green per §19.
Step 5 — Close. CC produces implementation notes at engine repo docs/phase-impl-notes/phase-59-implementation-notes-v0_1.md per §25. Applies refined close protocol with explicit refspecs per the methodology candidate from Phase 56 (manifest v0.41 §2 named principle). Applies Step 0 branch lifecycle per Phase 57-established convention: deletes phase-59-step-0 locally after merging Step 0 findings into the build branch context. Retroactive cleanup of any Phase 58 Step 0 branches present per Phase 57 P57-D7 Option A precedent.
Checkpoint C — Final, before tagging. CC pauses; final implementation-notes review; Operator confirms close-protocol invocation before tag lands. Tag phase-59-upload-pathway-completion on engine repo only (no OL tag per scoping v0.2 §What Phase 59 delivers).
Per P59-D9, four reserved amendment slots. Per scoping v0.2 §Stage 4.2 allocation:
Reserved-not-skipped: unconsumed if no amendment arises; the buffer's value is its presence, not its consumption. Phase 50 left all three reserved slots unconsumed; Phase 51, 52, 53, 54, 55, 56 followed; Phase 57 consumed three of four through three amendment cycles; Phase 58 consumed both of its two slots and breached at v0.5 (fifth amendment cycle requiring explicit Operator authorization per the established pattern). Phase 59's four-slot allocation doubles Phase 58's reservation because (a) v1-multi-pathway-collapses-inquiry-as-protocol is in its first application and protocol-shape surfacing is anticipated; (b) first-quality tuning in Step 3 inherently surfaces calibration shape. v0.2 confirms the slot count unchanged after 2f's addition — 2f fits within the existing wiring-half slot (slot 7) per the OL-contract-preservation risk-family reasoning.
Envelope-breach beyond slot 9 requires explicit Operator authorization per the Phase 58 v0.5 pattern. Mid-build amendment scoping runs in a separate Claude.ai chat per standard pattern; CC produces a halt-surface note per §22 and the Operator opens the amendment scoping chat.
The three-checkpoint structure follows the CR drafting handoff §6 — distinct from scoping v0.2 §Stage 3's two-checkpoint framing. The handoff added Checkpoint A (after Step 1 substrate work) because the Wave 7 substrate scope is large enough to warrant its own confirmation point before wiring half engages.
phase-59-upload-pathway-completion on engine repo only at Phase 59 close. No OL tag — Phase 59 has no OL work. OL tag returns at Phase 60 with phase-60-contribution-composer-completeness or analogous.
CC halts and writes a halt-surface note at phase-59-halt-surface-{timestamp}-v0_1.md in engine repo docs/phase-impl-notes/ on any of the following. Mid-build amendment scoping then runs in a separate Claude.ai chat per the standard pattern; CC does not draft-and-hope.
put_blob end-to-end surfaces async-context friction. Example: the put_blob call site is sync but ObjectStore is async (or vice versa); the wiring requires refactoring beyond Sub-arc 1b's single-call assumption. Halt; may require sub-arc refactor.put_blob reveals an ordering constraint scoping didn't anticipate. Per §8.3 — put_blob accepts only bytes and requires content-length, streaming can't provide content-length until the read completes. Halt for ordering decision; either 1b adapts to streaming-shape input or 1e refactors after 1b lands.AskOperatorForProcess shape and Sub-arc 2e's AskOperatorToClarify shape can't compose cleanly with the upload endpoint's response schema — e.g., both shapes need to fire on the same response and the schema can't represent the union, OR the detection registry's dispatch can't simultaneously support "best candidate with confidence" (for 2e) and "all candidates above threshold" (for 2f). Halt for amendment scoping.put_blob signature requires more than a single integration point in the upload endpoint flow. If the integration scope widens beyond Sub-arc 1b's single-call assumption — multiple call-sites needed, or upstream refactoring required — scoping v0.3 should absorb the wider surface before drafting continues. Halt at CR execution; the v0.2 CR's single-call assumption no longer holds.0064.AskOperatorForProcess naming feels wrong against the codebase's existing naming conventions that CRV-7's detection-registry read surfaces. If the codebase favors a different naming family (e.g., Disambiguation or Elicitation prefixes) rather than the "Ask Operator [preposition] [thing]" family AskOperatorToClarify establishes, naming-only divergence absorbs in-flight. If the naming conflict reveals an architectural mismatch (e.g., the codebase has no existing "ask the Operator" shape and 2e's AskOperatorToClarify would be the first), surface as a methodology candidate; don't halt unless the architectural shape itself is in question.In all cases, halt-and-surface is preferred to draft-and-hope. The amendment scoping path is established practice (Phase 49, 50, 51, 52, 53, 57, 58 all engaged it); engaging it is not failure.
Phase 59 closes when all of the following hold:
phase-59-upload-pathway-completion on engine repo at Phase 59 close. Annotated tag. Pushed to origin via the refined close protocol's explicit refspec (per manifest v0.41 §2 named principle). No OL tag.AskOperatorForProcess shape; per-candidate purpose-framing strings declared at registration; 2d pre-emption check; upload endpoint returns shape in response slot) alongside the existing list.0064 unless §22 halt fired for migration; in that case head at 0065 (or higher).docs/phase-impl-notes/phase-59-implementation-notes-v0_1.md per §25, capturing the trajectory, the Stage 2 verdicts under build pressure, reserved-slot consumption, methodology candidates for v0.21.phase-59-step-0 deleted locally per Phase 57-established convention. Any Phase 58 Step 0 branches still present at execution time also cleaned up per P57-D7 Option A retroactive cleanup pattern.AskOperatorToClarify shape; [NEW v0.2] multi-valid-prompt-for-process AskOperatorForProcess shape (with per-candidate purpose-framing strings); content-reliability three-state in ExtractionResult; fallback affordance endpoint response shape. Any drift surfaced per §22 and absorbed via amendment.Phase 60's OL surface implements consumption of Phase 59's engine-side contracts:
.DS_Store, Thumbs.db, etc. at OL folder-upload boundary.AskOperatorToClarify shape as an Operator-facing affordance with the candidate skill and confidence score; lets Operator confirm dispatch or pick a different skill or decline.AskOperatorForProcess data. Renders the elicitation affordance with per-candidate purpose-framing strings; lets Operator pick a candidate skill. Anticipated to be similar in chrome to but distinct in elicitation framing from the AskOperatorToClarify consumer — accuracy-question (2e) vs. purpose-question (2f). The two consumers consume from the same upload endpoint response slot (discriminated union); only one fires per upload depending on candidate landscape.unreliable_succeeded warning alongside the assertion content in the OL converse view; lets Operator review and decide.Phase 60 inherits Phase 59's contracts; the OL surface implements the consumption.
Per P59-D10. The v0.21 consolidation slot lives between Phase 59 close and Phase 60 scoping; runs parallel as a separate Claude.ai work session. Phase 59's implementation notes feed v0.21 with:
content_kind discriminator (Phase 35); assertion side doesn't. Phase 59 scoping surfaced but did not resolve. v0.21 names the asymmetry so future-phase decision can land.Per scoping v0.2 §What Phase 59 does NOT deliver, items deferred past Phase 59:
create_project.md plain-terms revision.origin/phase-* refs).
The queued-directions document (loomworks-queued-directions-and-deferred-work-v0_11.md at Phase 59 entry; will advance to v0.12 at Phase 59 close to absorb new candidates) carries these.
To be recorded in implementation notes (§25) and absorbed at Phase 59 close into queued-directions v0.12 if applicable. The CR cannot pre-enumerate these — they surface during build.
CC produces phase-59-implementation-notes-v0_1.md at engine repo docs/phase-impl-notes/ at Phase 59 close. The notes are the bridge from Phase 59 build determinations to manifest v0.42 absorption and v0.21 consolidation.
AskOperatorForProcess name as-shipped (whether the CR's resolution held or CRV-7 surfaced a codebase-naming-family divergence); the per-candidate purpose-framing strings declaration layer as-shipped (skill-registration vs. wherever); the 2d/2f composition check shape as-shipped (keyword-match, schema-check, or LLM call).AskOperatorForProcess to the contract list.The Discovery-record discipline applies to implementation notes. Where build experience contradicts a scoping verdict, the notes preserve both — the prior verdict (scoping v0.2 position), the build-time finding (what surfaced), and the resolution (how the surface was reconciled). The same discipline applies to any reserved-slot consumption — the original v0.2 framing, the friction that surfaced, the amendment-cycle scoping outcome.
v0.2 adds Move 5 as a recordable trajectory event itself. Implementation notes preserve: the v0.1 single-surface compression (confidence and purpose collapsed into the classifier confidence model); the v0.2 two-surface correction (P59-D4 covers confidence-disambiguation; P59-D11 adds purpose-disambiguation); the methodological lesson (the inquiry-as-protocol's vulnerability to scoping-time self-compression); and how build experience under v0.2's framing confirmed or refined the two-surface framing. This is the Phase 59-specific instance of the Discovery-record discipline applied to a CR-cycle event (not just a build-cycle event) — worth recording explicitly because the discipline previously applied predominantly to build trajectory; v0.2 extends it to CR-drafting trajectory.
This makes implementation notes the input to v0.42 manifest absorption (Stage 2 verdicts; reserved-slot consumption; methodology candidates) and to v0.21 consolidation (the six candidates above plus any surfaced).
DUNIN7 — Done In Seven LLC — Miami, Florida Phase 59 CR — Upload Pathway v1 completion — v0.2 — 2026-05-14 Supersedes v0.1 (preserved alongside as superseded per re-draft handoff §6).