Version. v0.1
Date. 2026-05-10
Author. Claude (drafting) / Marvin Percival (approving).
Target. /Users/dunin7/loomworks-engine on DUNIN7-M4 (MacMini M4). No work in loomworks (Operator Layer) or loomworks-marketing repos by default.
Baseline reference. Tag phase-52-jurisdiction-routing:
69aecdc (annotated tag object f11e41a); main HEAD at 69aecdc (Step 0 findings baseline check confirmed main HEAD = the tag commit; brief's phrasing of "one commit ahead at 14e5a7f" inverted the parent/child relationship — 14e5a7f is the parent of 69aecdc).DUNIN7/loomworks) Phase 51 marker tag at frontend e4c09e0 (sub-arc 2 has been empty across Phases 51 and 52).DUNIN7/loomworks-marketing) at bf2f694 (annotated tag object 76071b0); main one commit ahead at 78c26fa. Live at https://loomworks.doneinseven.com.
Phase 52 close per Step 0 findings: 2,137 substrate tests passed, 25 skipped, Alembic head 0064_phase_49_persons_columns_and_thresholds.py, working tree clean (engine); 139 vitest passed, 11 prerendered routes, eslint/tsc/build clean (Operator Layer, unchanged from Phase 50 baseline per Phase 51 V7 / Phase 52 P52-D4).
Priority. Standard.
Confidential. Internal DUNIN7.
Supersedes. No prior Phase 53 CR — v0.1 is the first.
CR number. CR-2026-068 is the expected value if Phase 52 was CR-2026-067. [CC verifies at Step 0 pre-flight item 1] against engine repo docs/phase-crs/ directory listing; advance if taken.
Companion to. loomworks-phase-53-scoping-note-v0_2.md (authoritative scope; absorbs Step 0 findings); loomworks-phase-53-cr-drafting-handoff-v0_1.md (drafting instructions for this CR); phase-53-step-0-findings-v0_1.md (engine repo docs/phase-impl-notes/; verified live-codebase state, absorbed into scoping v0.2 with one open FORAY question settled by this CR as P53-D12); phase-52-cr-jurisdiction-routing-v0_1.md (structural template); phase-50-cr-companion-as-authority-and-public-form-v0_1.md §10 (Memory event registration parallel); phase-31-cr-conversational-engagement-creation-v0_1.md (prompt template convention; Phase 31 backward-compat baseline); Phase 25 CR + Phase 25 FORAY amendment (induct_seed invocation pattern; _ANCHOR_PRIORITY seed-family precedent); Phase 16 CR + Phase 16 addendum (upload pipeline; _ACCEPTED extension point); current-status-manifest-v0_37.md.
Status. Pre-execution CR. Ready for Operator review and approval. Step 0 pre-flight runs against this version.
_ACCEPTED MIME-type extensionbuild_default_registry()discovery_to_seed_extractedPOST /engagements/{eid}/seed/extractPhase 53 opens the engagement creation arc — the second of the two architectural arcs left open after the credit / form-flow / marketing arc reached natural stabilization at Phase 52. The arc itself spans multiple phases; Phase 53 lays the foundational substrate so subsequent phases can build the Discovery-assistance product surface (queued directions §1.1), the Operator Layer integration, and ultimately the deferred Phase 32 marketing engagement on top. Phase 53 kickoff posture mirrors Phase 47 in the credit arc: open the arc with the load-bearing substrate primitive, defer the surface and methodology consolidation to subsequent phases.
Sub-arc 1 — Substrate (engine). Phase 53 lands the Discovery-to-seed skill as the engagement creation arc's foundational primitive — an LLM-assisted bounded transformation that takes a Discovery document landed in Memory via Phase 16's upload pipeline and produces a structured candidate Seed conforming to R-A5–R-A11. Four substantive surfaces ship together as sub-arc 1:
_ACCEPTED MIME-type extension — additive single-line change adding text/markdown to the upload pipeline's accepted set per V6 BREAKS / P53-D6. Markdown Discovery documents become uploadable via the existing POST /engagements/{eid}/files endpoint without any rebuild of Phase 16's pipeline.src/loomworks/skills/discovery_to_seed.py — conforms to the ExtractionSkill Protocol per V1 (file_path: Path, engagement_id, db, secret_key, person_id); reads the file's UTF-8 Markdown content; makes a single-shot LLM call with structured-output instructions targeting R-A5–R-A9 + R-A11 (R-A10 is endpoint-derived per Phase 25 §S2, not LLM-extracted); returns ExtractionResult with the structured draft_seed payload in extra_metadata and a human-readable extraction summary in text.discovery_to_seed_extracted Memory event — typed MemoryObject subclass mirroring Phase 25's seed_drafted family (added to _ANCHOR_PRIORITY for full FORAY namespace injection per P53-D12 Option A, not mirroring Phase 50/52's deferral); OBJECT_TYPE_REGISTRY entry deferred per Phase 50/52 precedent.POST /engagements/{eid}/seed/extract endpoint per P53-D10 / V10 — engagement-scoped; request body references the Phase 16 uploaded file by file_id; orchestrates: load file → invoke skill → read extra_metadata["draft_seed"] → resolve R-A10 (creating person as initial contributor; default registered agents) → write discovery_to_seed_extracted Memory event with full FORAY hooks → call induct_seed per Phase 25 canonical path → return result.
Sub-arc 2 — Operator Layer. Empty by default per P53-D4. Phase 51 V7 and Phase 52 P52-D4 precedent. The Operator Layer's existing Phase 31 conversational creation surface (five engagement-scoped endpoints per V5; produces a string brief, does NOT call induct_seed) remains unchanged. Phase 54+ may add an Operator Layer surface that exposes the Discovery-to-seed skill once the Discovery-assistance arc (queued directions §1.1) defines the upstream surface.
Sub-arc 3 — Marketing site. Empty. Phase 53 doesn't touch the marketing repo.
By the close of Phase 53, the Operator can upload a Markdown Discovery document to an engagement, invoke the Discovery-to-seed endpoint, and receive a candidate Seed that flows through the Phase 25 induction loop unchanged. The skill produces seeds from heterogeneous Discovery shapes via LLM-assisted bounded contract (P53-D1) — V8's evidence of 6+ organizing schemes across project-knowledge Discovery records made purely-declarative extraction architecturally unsuitable.
Tag at completion: phase-53-discovery-to-seed-skill on engine repo only.
Build-time estimate: 2–2.5 hours total (substrate sub-arc 1 only). Test count delta: ~20–30 over the Phase 52 close baseline of 2,137. No Alembic migration. Per V7's typed-MemoryObject-without-migration pattern.
Per scoping note v0.2 §3, fully transposed:
_ACCEPTED extension. Single-line additive change at src/loomworks/api/routers/files.py:102 (per V6 evidence) adding text/markdown to the union of accepted MIME types. No other Phase 16 changes; no migration; no breaking change to existing uploaders.src/loomworks/skills/discovery_to_seed.py. Conforms to ExtractionSkill Protocol per V1 evidence at src/loomworks/skills/registry.py:64–90. LLM-assisted single-shot bounded contract per P53-D1: no conversation, no multi-turn, no retry-on-clarification. Targets R-A5–R-A9 + R-A11; R-A10 is endpoint-derived. Returns ExtractionResult with structured draft_seed payload in extra_metadata.src/loomworks/prompts/discovery_to_seed_extraction.py, parallel to V5's Phase 31 src/loomworks/prompts/creation_conversation.py. Exports EXTRACTION_PROMPT, EXTRACTION_MODEL, PROMPT_VERSION constants.build_default_registry() at src/loomworks/skills/registry.py:165–195 (per V1 evidence) alongside the three existing (audio at line 177; PDF at 183; image at 189). Keyed by text/markdown per V1's content-type-pattern convention.discovery_to_seed_extracted Memory event. Typed MemoryObject subclass; Literal["discovery_to_seed_extracted"] object_type; schema per §10.3 below. _ANCHOR_PRIORITY entry added ("discovery_to_seed_extracted": "standard") per P53-D12 / V9 mechanics — mirrors Phase 25's seed-family (seed_drafted, seed_amended already in _ANCHOR_PRIORITY per V7 evidence). OBJECT_TYPE_REGISTRY entry deferred per Phase 50/52 precedent (P53-D9 / V7). No Alembic migration (V7).POST /engagements/{eid}/seed/extract endpoint per P53-D10 / V10. Engagement-scoped per universal /seed* convention (V10's 10 routes enumerated; all engagement-scoped). Request body: {file_id: UUID} referencing the Phase 16-uploaded Discovery document. Response body: extraction result + induction outcome. Orchestration sequence per §11.induct_seed hand-off. Endpoint terminal step calls induct_seed at src/loomworks/engagement/creation.py:409 per V5 evidence. Becomes the third production caller alongside src/loomworks/engagement/seed_amendment.py:133 and src/loomworks/api/routers/engagements.py:361.These items are explicitly deferred to Phase 54+ inside the engagement creation arc, deferred-pending-trigger, or out of Phase 53 entirely:
induct_seed; whether deliberate design or incompleteness is not a Phase 53 question. Phase 54+ for Operator clarification; if incompleteness, Phase 31 amendment lands at Phase 54+.OBJECT_TYPE_REGISTRY entry for discovery_to_seed_extracted (deferred per Phase 50/52 precedent / V7; Phase 54+ when downstream consumers built that need deserialize_memory_object("discovery_to_seed_extracted", payload)).draft_seed from the conversation router (V5's natural extension; Phase 54+ candidate noted in V5 findings)._author_proposal_assertion; Phase 42 turns reconciler coverage; reactivation in-session chrome; Operator content authoring; voice tuning; methodology v0.21 consolidation; operational items: engine deployment via M4 tunnel, mailbox provisioning, cloudflared upgrade).Phase 53 ships no Companion-rendered prose. The skill produces a structured Seed; the Memory event records a substrate fact; no voice surface. The CR has no §7-equivalent voice-template section. Same posture as Phase 52 §2.3.
DUNIN7/loomworks-engine): 2,137 tests passed, 25 skipped, Alembic head 0064_phase_49_persons_columns_and_thresholds.py, working tree clean on main. Tag phase-52-jurisdiction-routing annotated object f11e41a resolves to commit 69aecdc. main HEAD = 69aecdc (the tag commit). Per Step 0 findings baseline check.phase-52-jurisdiction-routing at engine 69aecdc; main one commit ahead at 14e5a7f" inverts the parent / child relationship — 14e5a7f is the parent of 69aecdc, so main is at the tag commit, not one commit ahead. Baseline state is "Phase 52 close, no commits since." None of the pre-flight items below depend on the resolution; recorded for consistency.DUNIN7/loomworks): unchanged from Phase 50 baseline per Phase 51 V7 / Phase 52 P52-D4. 139 vitest passed, 11 prerendered routes, eslint/tsc/build clean. Marker tag phase-51-marketing-site-and-companion-email at frontend e4c09e0. Not in Phase 53 scope by default per P53-D4.DUNIN7/loomworks-marketing): Tag phase-51-marketing-site-and-companion-email at bf2f694 (annotated tag object 76071b0); main one commit ahead at 78c26fa. Live at https://loomworks.doneinseven.com. Not in Phase 53 scope.DUNIN7/loomworks-ui): unchanged from Phase 48 baseline. Not in Phase 53 scope.If the baseline diverges (substrate test count off by more than ±2 routine noise, Alembic head different, working tree dirty, tag missing on engine, marketing site not live), CC stops at the start of Step 0 and reports before running any pre-flight items.
The scoping note v0.2 absorbed ten verifications run by CC against the live codebase, recorded in phase-53-step-0-findings-v0_1.md at engine repo docs/phase-impl-notes/. Consolidated state per the brief's framework: State 2 — one BREAKS plus enumeration findings; v0.2 is the architectural-amendment absorption. The CR proceeds against v0.2 with P53-D12 settling the FORAY question that v0.2 left open between its §3.5 schema framing and §6 V7 row.
| # | Verification | Verdict | Where absorbed |
|---|--------------|---------|----------------|
| V1 | ExtractionSkill family location, protocol, registration call-sites | HOLDS | scoping v0.2 §3 V1; §4 P53-D3 confirms candidate (i); §5.1, §5.2 |
| V2 | Materializer registry identity | HOLDS as distinct surface | scoping v0.2 §3 V2; §4 P53-D3 narrows by elimination (retires candidate (iii)) |
| V3 | Phase 38 declare-and-register pattern | HOLDS as sibling-but-distinct | scoping v0.2 §3 V3; §4 P53-D3 narrows by elimination (retires candidate (ii)-via-grammar) |
| V4 | Seed schema + R-A5–R-A11 mapping | HOLDS with naming-only divergence | scoping v0.2 §3 V4; §4 P53-D5 sharpened; §3.2 / §11 schema |
| V5 | Phase 31 conversational creation surface | HOLDS with minor drift + functional gap | scoping v0.2 §3 V5; §4 P53-D5 sharpened; §4 P53-D11 new; §3.7 backward-compat tests |
| V6 | Phase 16 upload pipeline (Markdown) | BREAKS | scoping v0.2 §3 V6; §4 P53-D6 amended; §3.1 amendment slice |
| V7 | Memory event registration pattern (Phase 50/52) | HOLDS with minor convention drift | scoping v0.2 §3 V7; §4 P53-D9 confirmed; v0.2 noted open FORAY question — settled at this CR by P53-D12 |
| V8 | Discovery records as test fixture inventory | PARTIALLY HOLDS | scoping v0.2 §3 V8; §4 P53-D1 validated; §3.7 test fixtures tightened |
| V9 | FORAY attestation seam | HOLDS | scoping v0.2 §3 V9; §10 schema; P53-D12 settles _ANCHOR_PRIORITY entry per V9 mechanics |
| V10 | Endpoint surface naming convention | HOLDS via in-convention | scoping v0.2 §3 V10; §4 P53-D10 new (settles /seed/extract) |
These ten verifications are not re-run at CR-execution time. The pre-flight Step 0 list in §3.3 covers CR-time deltas — exact strings, line numbers, module paths that the CR drafter specified against Step 0 evidence but where placement decisions or convention-fit questions remain.
Before Step 1 begins, CC archives this CR at docs/phase-crs/phase-53-cr-discovery-to-seed-skill-v0_1.md in the engine repo and runs the following pre-flight reads. Step 0 findings absorbed most exact-string verifications; this pre-flight list covers the few remaining placement / convention-fit confirmations.
CR-2026-068 is the next available CR number against engine repo docs/phase-crs/ directory listing; advance if taken._ACCEPTED exact line and adjacent test coverage. Per V6 evidence: _ACCEPTED = _ACCEPTED_AUDIO | _ACCEPTED_PDF | _ACCEPTED_IMAGE at src/loomworks/api/routers/files.py:102. Confirm the union expression line and locate any existing test that validates accepted / rejected types (likely in tests/test_phase_16_*.py) so §7's amendment to _ACCEPTED and the test addition at §17 land cleanly. If existing tests parametrize over _ACCEPTED, the new text/markdown member needs no further test scaffolding; if they enumerate explicitly, §17's smoke test adds the member.src/loomworks/skills/discovery_to_seed.py; V1 confirmed the skills directory is src/loomworks/skills/ containing __init__.py, image_description.py, pdf_extraction.py, registry.py, transcription.py. Confirm the new module lands as a sibling at the same level (no subdirectory needed) and the naming convention (snake_case, no _skill suffix per V1's sibling listing).src/loomworks/prompts/discovery_to_seed_extraction.py. V5 confirmed Phase 31's template at src/loomworks/prompts/creation_conversation.py (with PROMPT_VERSION = "1.0", CONVERSATION_MODEL = "claude-sonnet-4-6", CREATION_CONVERSATION_PROMPT, __all__ exports per lines 21, 23, 26, 200–203). Confirm the prompts directory layout and the convention (module-level constants exported via __all__).build_default_registry() at src/loomworks/skills/registry.py:165–195, with three registration calls at lines 177, 183, 189. §9's new fourth registration lands as a sibling — confirm the insertion site (ordering convention: by Content-Type family alphabetically, or by introduction order, or some other ordering; the CR specifies one but [CC verifies] against the existing call ordering)._ANCHOR_PRIORITY insertion point + seed-family proximity. Per V7 evidence: _ANCHOR_PRIORITY dict at src/loomworks/memory/events.py:112–130 with 14 current entries including seed_drafted, seed_amended, finding_produced, finding_addressed, induction_cycle_recorded. §10's new entry "discovery_to_seed_extracted": "standard" lands alongside the seed-family per P53-D12. Confirm the exact placement convention (insertion order: by phase, by family, alphabetical, or otherwise) so §10's amendment respects local ordering.src/loomworks/credit/grant_request.py and Phase 52 used src/loomworks/credit/jurisdiction_routing.py per V7 evidence — both credit-family events. Phase 53's discovery_to_seed_extracted is a seed-family event, not credit-family. Confirm placement convention: natural location is src/loomworks/engagement/discovery_to_seed.py (a new engagement-family file co-located with src/loomworks/engagement/creation.py which hosts draft_seed and induct_seed) or potentially in src/loomworks/skills/discovery_to_seed.py (co-located with the skill itself, though this mixes substrate concerns). Pre-flight selects placement; CR's §10 specifies loomworks/engagement/discovery_to_seed.py as default with [CC verifies at Step 0 pre-flight item 7] for the engagement-family placement question./seed/extract route placement. Per V10 evidence: 10 /seed* routes across 3 modules — engagements.py (5 routes: 215, 297, 343, 425, 822), seed_view.py (1 route: 34), seed_conversation.py (5 routes: 491, 813, 857, 877, 938). The closest sibling for /seed/extract is /seed/induct at engagements.py:343 (verb-aligned, lifecycle-action, post-seed-creation). Pre-flight confirms placement: extend engagements.py with the new route (default) or create new module src/loomworks/api/routers/seed_extraction.py if scope of orchestration warrants separation.induct_seed exact signature and call shape. Per V5 evidence: defined at src/loomworks/engagement/creation.py:409 (async def induct_seed(...)); two production callers at src/loomworks/engagement/seed_amendment.py:133 (outcome = await induct_seed(...)) and src/loomworks/api/routers/engagements.py:361 (same shape). §11 specifies the third invocation against verbatim sibling pattern — CC reads the full signature and one or both call shapes so the endpoint's hand-off lands exact.compute_content_hash exact import and _foray injection alignment. Per V9 evidence: compute_content_hash at src/loomworks/memory/events.py:133–145; _foray injection at events.py:209–220; columns at 102–104 (content_hash, attestation, foray_tx_ref). §10 specifies discovery_to_seed_extracted carries the full _foray namespace per P53-D12; CC confirms the append_event(event_kind="discovery_to_seed_extracted", payload=...) call site automatically picks up the _ANCHOR_PRIORITY entry and injects _foray per the existing mechanism. No new FORAY plumbing needed.system_config (mirroring V5's Phase 31 evidence — migrations/versions/0048_phase_31_system_config.py schema; helpers in src/loomworks/system_config/store.py:71,124,150). Confirm the canonical key name to use for the extraction model's API key — likely anthropic_api_key or a parallel new key — against existing system_config rows. If a new key is needed, §8 adds a row insertion via the existing set_system_config helper at deploy time; not via Alembic migration.If any of items 1–11 reveal architectural divergence (not just naming or exact-string placement), CC halts and surfaces — does not draft and hope. Naming-only divergences absorb in-flight per the standard discipline.
Archive this CR at:
/Users/dunin7/loomworks-engine/docs/phase-crs/phase-53-cr-discovery-to-seed-skill-v0_1.md
before Step 1 begins. Per Phase 47 / 48 / 49 / 50 / 51 / 52 standard pattern.
Twelve decisions settled in scoping note v0.2 §5 plus P53-D12 settled at this CR (the FORAY question v0.2 left open between its §3.5 schema framing and §6 V7 row). CC executes against them; does not re-decide. No decisions remain [Operator confirms] at CR-execution time.
P53-D1. LLM-assisted bounded skill. Per V8 evidence (~12 Discovery records across 6+ distinct organizing schemes — Decision-list, Thread-list, Numbered-position, Field-list R-A-aligned, Discovery-trajectory, Programme-shaped). Purely-declarative extraction would over-fit to one scheme; the LLM does real interpretation work to map heterogeneous Discovery shapes onto R-A5–R-A11. The discipline is bounded contract, not no LLM. Single-shot LLM call wrapped by strict input/output contract: no conversation, no multi-turn, no retry-on-clarification.
P53-D2. Free-form Markdown Discovery documents with recognized section headers (path (a)). Phase 53 consumes the kind of Discovery records already produced in chat. Phase 54+ may add a structured Memory-shape Discovery input (path (b)) as an alternative; not Phase 53 scope.
P53-D3. Skill registers in ExtractionSkillRegistry at src/loomworks/skills/registry.py (V1 confirmed location). Settled by elimination: V2 retired the materializer-registry candidate (render-output-side, distinct surface); V3 retired GRAMMAR_REGISTRY (shape-declarative not callable, raises on duplicate). The skill conforms to the ExtractionSkill Protocol exactly as V1 confirmed; registration adds a fourth entry in build_default_registry() alongside the three existing (audio, PDF, image).
P53-D4. Operator Layer sub-arc 2 empty by default. Per Phase 51 V7 / Phase 52 P52-D4 precedent. The Operator Layer's existing Phase 31 conversational creation surface (5 engagement-scoped endpoints per V5) remains unchanged. Phase 54+ may add an Operator Layer surface that exposes the Discovery-to-seed skill.
P53-D5. Phase 31 and Phase 53 coexist as truly distinct paths. V4 + V5 revealed deeper divergence than v0.1 anticipated: Phase 31 produces a string brief (stored on companion_turn events at seed_conversation.py:468, 842, 850); Phase 53 produces a structured Seed payload directly. Phase 31 does NOT call induct_seed (V5 confirmed only 2 production callers, neither in seed_conversation.py); Phase 53 does (becomes the third caller). They share the /engagements/{eid}/seed/* URL family (V10) but produce different artifacts and follow different downstream pipelines. Coexistence holds.
P53-D6. Phase 16 upload-pipeline as Discovery-document Memory location, via additive text/markdown MIME-type extension. V6 BREAKS: _ACCEPTED (at files.py:102) does NOT currently include text/markdown; Markdown uploads 415-reject at the upload boundary. Smallest-amendment path per V6 implication option (1): extend _ACCEPTED to include text/markdown; UTF-8 text-decoding happens inside the skill body. No rebuild of Phase 16's pipeline; no breaking change to existing uploaders. The alternative (V6 option (2) — Discovery-record-as-Memory-shape upstream substrate) is held for Phase 54+.
P53-D7. Tag name: phase-53-discovery-to-seed-skill on engine repo only. No tags on Operator Layer or marketing repos by default — Phase 53 has no work in those repos. If sub-arc 2 lands actual work via Phase 54 surface design lifting forward, marker tag follows Phase 51 V7 precedent (Operator Layer tag lands on the Phase 51 commit as a marker).
P53-D8. Engagement creation arc as explicit multi-phase trajectory. Phase 53 = kickoff (substrate primitive). Phase 54+ candidates listed in §18: Discovery-assistance flow (queued directions §1.1); Discovery-trajectory + Discovery-record specialists; Operator Layer engagement-creation surface; Phase 31 induct-seed gap clarification (P53-D11); Phase 31/53 unification or deprecation; eventual Phase 32 marketing engagement. Each phase scoped independently when it opens; arc framing in scoping language only, not commitment.
P53-D9. Memory event kind: discovery_to_seed_extracted. Typed MemoryObject subclass; Literal["discovery_to_seed_extracted"] object_type; schema per §10.3. Mirrors Phase 25's seed-family naming and structural pattern. OBJECT_TYPE_REGISTRY entry deferred per Phase 50/52 precedent (V7) — substrate event functions without explicit registration; Phase 54+ adds when downstream consumers need deserialize_memory_object("discovery_to_seed_extracted", payload). For the FORAY question, see P53-D12 below.
P53-D10. Endpoint name: POST /engagements/{eid}/seed/extract. Per V10's tighter-alternative recommendation. The endpoint is an ExtractionSkill invocation surface; the URL reflects the family. Verb-aligned with /seed/induct per V10's lifecycle-action convention. Engagement-scoped per V10's universal /seed* family pattern (10 routes enumerated, all engagement-scoped, no system-scoped sibling family).
P53-D11. Phase 31 induct-seed gap deferred to Phase 54+ for Operator clarification. V5 found Phase 31 produces a seed_document: str brief stored on companion_turn events and does NOT call induct_seed; entering the Phase 25 induction loop from Phase 31 currently requires a separate Phase 25 amend step (the Operator extracts structured fields from the brief and POSTs to /engagements/{eid}/seed). Whether this is deliberate design (string-brief flow is its own completion path) or incompleteness (Phase 31 should call induct_seed but doesn't) is not a Phase 53 question. Phase 53 carry-forward records the open question; no Phase 31 amendment in Phase 53.
P53-D12. discovery_to_seed_extracted added to _ANCHOR_PRIORITY at priority "standard". Settled at this CR; not in v0.2. The scoping note v0.2 left an internal mechanical contradiction between its §3.5 schema framing ("_foray namespace + content_hash (per V9 canonical pattern)") and its §6 V7 row ("Defer OBJECT_TYPE_REGISTRY + _ANCHOR_PRIORITY per Phase 50/52"). Per V9 evidence (events.py:209–220), _foray is injected into the event payload only when _ANCHOR_PRIORITY.get(event_kind) is not None. The two readings are mechanically irreconcilable: either Phase 53 mirrors Phase 50/52 (defer entry, no _foray namespace, just column-level content_hash) or it mirrors Phase 25's seed-family (seed_drafted, seed_amended already in _ANCHOR_PRIORITY per V7 — entry present, full _foray namespace injection). Settled: mirror Phase 25's seed-family. discovery_to_seed_extracted is a seed-lineage event the way seed_drafted and seed_amended are, not an authority/credit event the way grant_request_received and jurisdiction_routing_decided are. The §3.5 schema framing is the operative commitment; the §6 V7 row's "Phase 50/52 precedent" framing pattern-matched on the wrong cousin (V7 findings flagged exactly this). Add "discovery_to_seed_extracted": "standard" to _ANCHOR_PRIORITY at events.py:112–130. OBJECT_TYPE_REGISTRY remains deferred separately per P53-D9 (those are independent registrations per V7's clarification).
No Alembic migration. Per V7 evidence: Phase 50 and Phase 52 both added typed MemoryObject subclasses (with Literal[...] object_type and field definitions) without introducing a migration. The substrate's memory_events table already carries content_hash, attestation, foray_tx_ref columns per V9 evidence at src/loomworks/memory/events.py:102–104. Phase 53's discovery_to_seed_extracted event lands in the same column shape.
Alembic head remains 0064_phase_49_persons_columns_and_thresholds.py at Phase 53 close.
P53-D12's _ANCHOR_PRIORITY entry is a code-only addition to a module-level dict at events.py:112–130; no schema implications.
The system_config row for the LLM API key (if needed per §8 / pre-flight item 11) is a row insertion via the existing set_system_config helper, not a migration.
High-level summary; full specs in §§7–11. All changes in DUNIN7/loomworks-engine only.
| # | What | Path | Per §§ |
|---|------|------|--------|
| 1 | Phase 16 _ACCEPTED MIME-type extension (additive) | src/loomworks/api/routers/files.py:102 | §7 |
| 2 | Discovery-to-seed skill module (new) | src/loomworks/skills/discovery_to_seed.py | §8 |
| 3 | Prompt template module (new) | src/loomworks/prompts/discovery_to_seed_extraction.py | §8 |
| 4 | Skill registration in build_default_registry() | src/loomworks/skills/registry.py:165–195 | §9 |
| 5 | discovery_to_seed_extracted Memory event class (new) | src/loomworks/engagement/discovery_to_seed.py [CC verifies placement at Step 0 pre-flight item 7] | §10 |
| 6 | _ANCHOR_PRIORITY entry addition | src/loomworks/memory/events.py:112–130 | §10.4 |
| 7 | POST /engagements/{eid}/seed/extract endpoint | src/loomworks/api/routers/engagements.py [CC verifies placement at Step 0 pre-flight item 8] | §11 |
No Operator Layer changes (§12). No marketing repo changes (§13).
_ACCEPTED MIME-type extension
Extend _ACCEPTED at src/loomworks/api/routers/files.py:102 to include text/markdown. Single-line additive change per P53-D6 / V6.
V6 enumerated the current accepted-set composition:
_ACCEPTED_AUDIO at files.py:69–79: 9 audio MIME types._ACCEPTED_PDF = {"application/pdf"} at line 80._ACCEPTED_IMAGE at lines 92–101: 8 image MIME types._ACCEPTED = _ACCEPTED_AUDIO | _ACCEPTED_PDF | _ACCEPTED_IMAGE at line 102. Total: 18 MIME types.415 Unsupported Media Type if content_type not in _ACCEPTED. The reject error message lists the allowed set verbatim.
text/markdown, text/plain, text/x-markdown are NOT in _ACCEPTED. Markdown Discovery uploads via POST /engagements/{engagement_id}/files currently 415-reject at the upload boundary.
Two approaches — naming-only choice for the CR drafter; CC selects at Step 1 per local convention:
Option (i) — Extend the union directly.
# src/loomworks/api/routers/files.py:102 — current
_ACCEPTED = _ACCEPTED_AUDIO | _ACCEPTED_PDF | _ACCEPTED_IMAGE
# Phase 53 amendment — option (i)
_ACCEPTED_MARKDOWN = {"text/markdown"} # NEW IN PHASE 53 — P53-D6 / V6
_ACCEPTED = _ACCEPTED_AUDIO | _ACCEPTED_PDF | _ACCEPTED_IMAGE | _ACCEPTED_MARKDOWN
Option (ii) — Inline literal in the union.
# Phase 53 amendment — option (ii)
_ACCEPTED = _ACCEPTED_AUDIO | _ACCEPTED_PDF | _ACCEPTED_IMAGE | {"text/markdown"}
Option (i) is the more conventional style given the existing _ACCEPTED_AUDIO / _ACCEPTED_PDF / _ACCEPTED_IMAGE named constants — symmetry suggests _ACCEPTED_MARKDOWN. Option (ii) is tighter for a single-element set but loses the named-constant symmetry. Default: Option (i). CC selects at Step 1 per local convention.
text/markdown only
Not text/plain, not text/x-markdown, not other text types. Per scoping v0.2 §3.1: smallest additive change to unblock Discovery-document upload. Phase 54+ can extend further if needed.
The skill at §8 declares its accepted content type as text/markdown exactly (matching the _ACCEPTED member); the skill's registration in §9 uses the same string as the content-type-pattern key.
The amendment is purely additive — existing MIME types continue to be accepted; existing reject behavior preserved for non-_ACCEPTED types. The error message at lines 191–201 lists the allowed set verbatim; after Phase 53, the message includes text/markdown in the listing automatically (it's the same set being listed). No callers of the upload endpoint need updating; the change is invisible to them.
One additive smoke test in §17 confirms text/markdown upload returns 201 and produces an UploadedFileRow referenceable from §11's endpoint. Existing test coverage of _ACCEPTED reject behavior continues unchanged (those tests assert specific non-_ACCEPTED types reject; text/markdown is no longer one of them).
Per V6 evidence: the upload endpoint persists bytes and an UploadedFileRow metadata row; no Memory event is written by the upload endpoint itself. The two-step pattern per the route docstring (files.py:173–181): upload stages the file, then a separate contribution / extraction endpoint references the file_id and dispatches downstream. Phase 53 follows this exactly — §11's endpoint is the "second step" that references the Phase 16-staged file_id.
New file src/loomworks/skills/discovery_to_seed.py hosting the LLM-assisted bounded skill that transforms a Markdown Discovery document into a structured candidate Seed payload. Conforms to the ExtractionSkill Protocol per V1 evidence.
Per V1 evidence at src/loomworks/skills/registry.py:64–90, the Protocol is:
class ExtractionSkill(Protocol):
async def __call__(
self,
*,
file_path: Path,
engagement_id: UUID,
db: AsyncSession,
secret_key: str | None = None,
person_id: UUID | None = None,
) -> ExtractionResult: ...
The Phase 53 skill conforms to this signature exactly. Input is file_path: Path, not pre-decoded text — UTF-8 decoding happens inside the skill body. (The scoping note v0.2 §3.2 described the input as discovery_document_text: str; the protocol-conforming shape is file_path: Path with internal decoding. Decoding-at-the-skill matches the convention of the existing skills — PDF, image, audio — which all read their content from the provided file path.)
Per V1 evidence at src/loomworks/skills/registry.py:28–61:
@dataclass(frozen=True)
class ExtractionResult:
text: str
method: str
source_mode: str
key_source: Literal["operator", "person", "system"] | None = None
extra_metadata: dict[str, object] | None = None
The Phase 53 skill returns:
text: a human-readable extraction summary (e.g., "Extracted candidate seed targeting R-A5–R-A9 + R-A11 from Discovery document of 2,400 words; 3 R-A fields have explicit gap markers."). Brief; not the structured payload.method: "discovery_to_seed_extraction".source_mode: "markdown".key_source: "system" (per §8.7's system-level key resolution).extra_metadata: dict carrying {"draft_seed": <Seed payload dict>, "skill_version": "1.0", "gap_fields": [<R-A identifiers>], "prompt_version": EXTRACTION_PROMPT_VERSION}.
The structured draft_seed payload in extra_metadata is the canonical Phase 53 output. §11's endpoint reads extra_metadata["draft_seed"] and uses it to drive Memory event write + induct_seed hand-off.
1. Open file_path; read bytes; UTF-8 decode → discovery_document_text: str.
2. Resolve LLM API key: secret_key kwarg if provided; otherwise system-level
key via get_system_config(...) per §8.7.
3. Build prompt: format EXTRACTION_PROMPT with discovery_document_text
substitution.
4. Single-shot LLM call (no conversation; no retry-on-clarification):
model=EXTRACTION_MODEL, structured-output instructions targeting
R-A5–R-A9 + R-A11 fields per Seed schema (V4 evidence).
5. Parse LLM response; validate against Seed payload schema; identify
gap_fields (any R-A targeted field the LLM left blank or marked
"[not extractable from this Discovery]").
6. Construct ExtractionResult(text=<summary>, method="...", source_mode="markdown",
key_source="system", extra_metadata={...}).
7. Return.
Per V4 evidence: canonical Seed model (src/loomworks/engagement/models.py or per V4's Seed definition site) has fields:
what_the_work_is: str (R-A5)who_consumes_the_work: str (R-A6)voice_of_the_work: str (R-A7)constraints: list[str] (R-A8)success_conditions: str (R-A9)initial_contributors: list[UUID] (R-A10 — not LLM-extracted; see §8.6)initial_agents: list[UUID] (R-A10 — not LLM-extracted; see §8.6)additional_assertions: dict[str, str] (R-A11)
The LLM extraction targets six fields: what_the_work_is, who_consumes_the_work, voice_of_the_work, constraints, success_conditions, additional_assertions. Each field's prompt instruction describes the R-A requirement (per Phase 25 §S2 wording and the structured-input-template.md guidance) and asks for the LLM to extract from the Discovery document or leave with an explicit gap marker.
[CC verifies at Step 0 pre-flight] exact Seed field names against V4 evidence (V4 confirmed naming-only divergence; CR uses verbatim field names).
Per Phase 25 §S2 evidence: "R-A10 (initial contributors and agents) is not a form field. The creating person is the initial contributor. Agents are auto-registered at commit time. R-A10 content is derived, not authored."
Phase 53 inherits this convention: the skill does NOT extract initial_contributors or initial_agents from the Discovery document. The skill's draft_seed payload leaves these fields empty ([]); §11's endpoint fills them at hand-off time:
initial_contributors = [<creating_person_id>] (the actor invoking the extract endpoint).initial_agents = <default registered agents> per Phase 25 §S2 auto-registration convention; [CC verifies at Step 0 pre-flight] the default-agent-resolution helper location and convention used by Phase 25's commit path.
Per V5 evidence: Phase 31's conversational creation uses system_config for the LLM API key (migrations/versions/0048_phase_31_system_config.py schema; set_system_config / get_system_config / delete_system_config helpers at src/loomworks/system_config/store.py:71,124,150). Phase 53 follows the same convention.
The skill resolves the LLM key by:
secret_key kwarg is non-None (as provided by the ExtractionSkillRegistry dispatch path with a per-engagement key per V1's protocol), the skill uses it. (This path is not exercised by §11's endpoint — see below — but the skill remains protocol-conformant for hypothetical contribution-pipeline invocations.)get_system_config(db, key_name=<canonical_key>) to fetch the system-level LLM API key. [CC verifies at Step 0 pre-flight item 11] the canonical key name in current system_config table (likely anthropic_api_key or parallel; mirror Phase 31's convention).
§11's endpoint always invokes the skill with secret_key=None, triggering the system-level resolution path. This is the canonical Phase 53 invocation; the protocol-conformant per-engagement path exists for future flexibility but is not exercised in Phase 53 production code.
New file src/loomworks/prompts/discovery_to_seed_extraction.py, parallel to V5's Phase 31 creation_conversation.py.
Module-level constants (mirroring Phase 31's convention per V5 evidence — PROMPT_VERSION = "1.0", CONVERSATION_MODEL = "claude-sonnet-4-6", CREATION_CONVERSATION_PROMPT defined inline, __all__ exports):
# src/loomworks/prompts/discovery_to_seed_extraction.py
PROMPT_VERSION = "1.0"
EXTRACTION_MODEL = "claude-sonnet-4-6" # [CC verifies against Phase 31 convention at Step 0 pre-flight item 4]
EXTRACTION_PROMPT = """\
... (multi-line literal: instructions for the LLM to extract R-A5–R-A9 + R-A11
from a Markdown Discovery document, with structured-output JSON shape, gap
markers for non-extractable fields, and explicit anti-hallucination guidance) ...
"""
__all__ = ["EXTRACTION_MODEL", "EXTRACTION_PROMPT", "PROMPT_VERSION"]
The prompt content itself is voice-shaped at draft time; the CR specifies the structural commitments (R-A5–R-A9 + R-A11 fields targeted; explicit gap markers; anti-hallucination instructions) but the literal prose is built at Step 2.
Per P53-D1: single-shot LLM call; no conversation; no multi-turn; no retry-on-clarification. If the input Discovery document is too sparse for a high-quality Seed, the induction loop catches the gaps (Phase 25 §R-A14 — "Each finding produced by the seed-induction agent MUST name the requirement not met, the location in the seed where the gap was detected, and a suggestion (not a decision) about what would satisfy the requirement").
The skill leaves fields gap-marked rather than fabricating. Test 4c (§17) asserts this behavior on a Discovery record that doesn't have content for a specific field.
PROMPT_VERSION = "1.0" per Phase 31's convention. Future prompt iterations bump the version; the version is captured on each Memory event write per §10.3 (skill_version field). Phase 25's induction-loop history reads seed_requirements_engagement_version_at_induction; Phase 53's audit trail reads skill_version for prompt-version provenance.
build_default_registry()
Add a fourth registration call to build_default_registry() at src/loomworks/skills/registry.py:165–195 per V1 evidence. The new skill registers under content-type-pattern "text/markdown".
Three production registrations in build_default_registry() (per V1 evidence enumerating call-sites at registry.py:177, 183, 189):
registry.register("audio/", transcribe_audio_skill, label="Audio (voice recording or upload)", mode="voice") at line 177.registry.register("application/pdf", extract_pdf_skill, label="PDF document", mode="pdf") at line 183.registry.register("image/", describe_image_skill, label="Image (photo, diagram, screenshot)", mode="image") at line 189.
# src/loomworks/skills/registry.py — inside build_default_registry()
# Insertion point: alongside the three existing calls; ordering convention
# [CC verifies at Step 0 pre-flight item 5].
from loomworks.skills.discovery_to_seed import extract_discovery_to_seed
registry.register(
"text/markdown",
extract_discovery_to_seed,
label="Discovery document (Markdown)",
mode="discovery",
) # NEW IN PHASE 53 — P53-D3 / V1
Per V1's content-type-pattern keying: "text/markdown" matches uploads with that exact content type (per the canonicalization at files.py:105–119 — strips ; codec=... parameters, lowercases). The registry's longest-match-wins resolution (per V1 evidence at registry.py:119–145) means "text/markdown" resolves cleanly against the canonicalized upload content type.
Exact placement: [CC verifies at Step 0 pre-flight item 5]. Naming-only divergences (e.g., parameter ordering, mode-string convention) absorb in-flight per the standard discipline.
The registration creates a content-type-based dispatch hook: a POST to /engagements/{eid}/contributions referencing a file_id with content type text/markdown would route through ExtractionSkillRegistry.resolve("text/markdown") to the new skill per V6 evidence (src/loomworks/api/routers/contributions.py:97 dispatches via the resolved skill).
Phase 53's primary invocation path is §11's dedicated /seed/extract endpoint, not the contribution endpoint. The endpoint imports and invokes extract_discovery_to_seed directly. The registry-based dispatch path exists for hypothetical future use and remains protocol-conformant; if exercised at runtime (e.g., by an Operator POSTing a Markdown file to the contribution endpoint instead of the extract endpoint), the result lands as a standard contribution (text content stored per Phase 16's contribution flow), without the Phase 53 downstream pipeline (Memory event write + induct_seed hand-off). The contribution-pipeline-as-side-door is acceptable alpha posture; Phase 54+ can decide whether to special-case it or leave as-is.
§17 test 2a asserts the registration is present (build_default_registry().resolve("text/markdown") returns a RegisteredSkill with the correct skill function).
discovery_to_seed_extracted
Register a new typed MemoryObject subclass for the discovery_to_seed_extracted event, mirroring Phase 25's seed-family structural pattern with full FORAY namespace injection per P53-D12. Module placement: src/loomworks/engagement/discovery_to_seed.py (engagement-family co-located with Phase 25's creation.py; [CC verifies at Step 0 pre-flight item 7]).
V7 confirmed the typed-MemoryObject-subclass-without-migration pattern from Phase 50/52. Phase 53's class structure follows that pattern verbatim. Where Phase 53 diverges from Phase 50/52 — and follows Phase 25's seed-family instead — is in the _ANCHOR_PRIORITY entry: included per P53-D12, not deferred. (V7 itself flagged this as the open question; P53-D12 settles to inclusion per V9 mechanics and seed-family precedent.)
The mirror covers four elements:
MemoryObject subclass — DiscoveryToSeedExtracted(MemoryObject) with explicit fields per §10.3.Literal[...] object_type — Literal["discovery_to_seed_extracted"] matching the event-kind literal.append_event(event_kind="discovery_to_seed_extracted") call site in the endpoint at §11.4._ANCHOR_PRIORITY entry — "discovery_to_seed_extracted": "standard" added to the dict at events.py:112–130 per P53-D12; this triggers _foray namespace injection per V9 mechanics at events.py:209–220.OBJECT_TYPE_REGISTRY entry deferred per Phase 50/52 precedent (V7) — the typed shape exists for write-time validation and read-time clarity; explicit registration is deferred until a downstream consumer needs deserialize_memory_object on it.
# src/loomworks/engagement/discovery_to_seed.py
# [CC verifies module placement at Step 0 pre-flight item 7]
from datetime import datetime
from typing import Literal
from loomworks.memory.base import MemoryObject
# [CC verifies exact import path at Step 0 pre-flight item 7]
class DiscoveryToSeedExtracted(MemoryObject):
"""
Memory event recording an LLM-assisted extraction of a candidate Seed
from a Markdown Discovery document via Phase 53's discovery-to-seed
skill.
Mirrors Phase 25's seed-family structural pattern (typed MemoryObject
subclass; Literal[...] object_type; full FORAY namespace via
_ANCHOR_PRIORITY entry per P53-D12). OBJECT_TYPE_REGISTRY entry
deferred per Phase 50/52 precedent (V7).
"""
object_type: Literal["discovery_to_seed_extracted"] = "discovery_to_seed_extracted"
# The Phase 16 UploadedFileRow that holds the source Discovery document
discovery_document_ref: str # file_id (UUID as string)
discovery_document_filename: str # original_filename from UploadedFileRow
# The structured Seed payload the skill produced (dict serialization of
# Seed fields per V4 evidence; not a Seed instance because R-A10 fields
# are filled by the endpoint at hand-off time, not by the skill)
extracted_seed_payload: dict # {what_the_work_is, who_consumes_the_work, ...}
# Provenance of the extraction
skill_version: str # e.g., "1.0" per §8.10
prompt_version: str # e.g., "1.0" per Phase 31 PROMPT_VERSION convention
extraction_model: str # e.g., "claude-sonnet-4-6" per §8.8
# Gap markers: R-A identifiers the skill left empty / explicitly gap-marked
gap_fields: list[str] # e.g., ["R-A8", "R-A11"]
extracted_at: datetime
Exact field-order convention, base-class import path, frozen / mutable posture, datetime tz handling: [CC verifies at Step 0 pre-flight item 7] against Phase 25's seed-family classes and against Phase 50's GrantRequestReceived / Phase 52's JurisdictionRoutingDecided. Naming-only divergences from convention absorb in-flight.
_ANCHOR_PRIORITY entry per P53-D12
Per P53-D12 and V9 evidence (events.py:209–220 — _foray injection triggered by _ANCHOR_PRIORITY.get(event_kind) is not None):
# src/loomworks/memory/events.py:112–130 — current
# Per V7 evidence: 14 entries including seed_drafted, seed_amended,
# finding_produced, finding_addressed, induction_cycle_recorded, etc.
_ANCHOR_PRIORITY: dict[str, str] = {
"engagement_committed": "critical",
"engagement_committed_divergent": "critical",
"membership_created": "high",
# ... existing seed-family entries ...
"seed_drafted": "standard",
"seed_amended": "standard",
"finding_produced": "standard",
"finding_addressed": "standard",
"induction_cycle_recorded": "standard",
# ... etc ...
"discovery_to_seed_extracted": "standard", # NEW IN PHASE 53 — P53-D12 / V9
}
[CC verifies exact ordering at Step 0 pre-flight item 6] — placement alongside the existing seed-family entries per local ordering convention.
The "standard" priority mirrors seed_drafted and seed_amended. _foray namespace injection happens automatically per the existing mechanism at events.py:209–220 — no new FORAY plumbing.
OBJECT_TYPE_REGISTRY entry (deferred per P53-D9)
Per V7 evidence at src/loomworks/memory/registry.py:7–9, the registry currently holds:
OBJECT_TYPE_REGISTRY: dict[str, type[MemoryObject]] = {"generic": GenericMemoryObject}
with helper functions _register_phase_2_types through _register_phase_37_types registering Phase 2/3/4/6/7/9/10/11/31/34/37 types at module import. Phase 41+ types (including Phase 50/52) are NOT registered — V7 evidence confirmed Phase 50 and Phase 52 explicitly deferred registry inclusion per the "typed shape exists for write-time validation and read-time clarity; explicit OBJECT_TYPE_REGISTRY registration is deferred until a downstream consumer needs deserialize_memory_object" rationale.
Phase 53 follows the same deferral per P53-D9. The Phase 53 DiscoveryToSeedExtracted class exists; it functions for write-time event construction; the registry omission means deserialize_memory_object("discovery_to_seed_extracted", payload) returns the generic MemoryObject shape rather than the typed subclass — acceptable because no Phase 53 consumer needs the typed deserialization. Phase 54+ adds the registry entry if a downstream Discovery-trajectory specialist or Operator Layer surface needs typed deserialization.
The event-kind literal "discovery_to_seed_extracted" follows the snake_case past-tense convention from Phase 25's seed-family (seed_drafted, seed_amended, finding_produced) and from Phase 50/52 (grant_request_received, jurisdiction_routing_decided). [CC verifies convention at Step 0 pre-flight item 7] — if a different canonical form is in use elsewhere (e.g., a string-constant module enumerating event kinds), the literal lands in that location alongside Phase 25's existing entries.
Memory event registration is a code-only addition. The events themselves are appended to the existing event log via append_event (§11.4). No new tables, no new columns, no Alembic migration. Per §5.
The _ANCHOR_PRIORITY entry addition is also code-only — a dict mutation at module load.
Per V9 evidence: content_hash is written unconditionally to the memory_events.content_hash column on every event (events.py:236). The _foray namespace injection at events.py:209–220 happens for any event kind in _ANCHOR_PRIORITY. Phase 53's entry in _ANCHOR_PRIORITY (per §10.4 / P53-D12) ensures discovery_to_seed_extracted events get:
content_hash populated in the column (universal)._foray block in the payload: {"anchorable": True, "anchor_priority": "standard", "content_hash": <sha256>, "attestation_ref": None}.
§17 test 5 covers FORAY-hook assertion mirroring tests/test_phase_25_content_hash_and_foray.py (V9 canonical fixture).
POST /engagements/{eid}/seed/extractThis is the substantive substrate section. Per scoping v0.2 §3.4 and P53-D10 / V10.
The endpoint is a synchronous orchestrator that bridges a Phase 16-uploaded Discovery document into the Phase 25 induction loop. Distinguishing characteristics:
/seed* family pattern (10 routes enumerated, all engagement-scoped)./seed/induct per V10's lifecycle-action convention. The endpoint extracts a candidate seed; /seed/induct then runs induction over it.extract_discovery_to_seed from §8 and invokes it directly (not via ExtractionSkillRegistry.resolve()). The registry registration in §9 exists for content-type-based dispatch flexibility; the endpoint's direct invocation is the canonical Phase 53 path.discovery_to_seed_extracted per §10; full FORAY namespace per P53-D12.induct_seed hand-off — the endpoint becomes the third production caller of induct_seed per V5 (alongside seed_amendment.py:133 and engagements.py:361).
This is architecturally distinct from Phase 25's /engagements/{eid}/seed/induct (which inducts an already-drafted seed from form fields) and from Phase 31's conversational flow (which produces a string brief, not a structured Seed — V4 + V5). Phase 53's endpoint produces a new candidate seed from a Discovery document, then immediately inducts it.
Per V10 evidence: 10 /seed* routes across 3 modules. The closest sibling for /seed/extract is /seed/induct at src/loomworks/api/routers/engagements.py:343 (engagements.py hosts 5 of the 10 routes; verb-aligned; lifecycle-action; post-seed-creation).
Default placement: extend src/loomworks/api/routers/engagements.py with the new route, positioned after the /seed/induct route handler. [CC verifies at Step 0 pre-flight item 8] — if engagements.py is already large (V10 enumerated routes at lines 215, 297, 343, 425, 822 — spread across 600+ lines), or if the orchestration logic warrants module separation, create new module src/loomworks/api/routers/seed_extraction.py with the single route. Default = engagements.py.
# Request body
class ExtractSeedRequest(BaseModel):
file_id: UUID # references UploadedFileRow from Phase 16 upload
# Optional: future amendments (e.g., extraction-specific prompts) land
# additively without breaking signature.
# Response body
class ExtractSeedResponse(BaseModel):
extracted_seed_payload: dict # the Seed-shaped dict the skill produced
seed_id: UUID # the inducted seed's ID after induct_seed hand-off
induction_outcome: dict # the InductionOutcome shape from induct_seed
discovery_to_seed_event_id: UUID # the discovery_to_seed_extracted Memory event ID
gap_fields: list[str] # R-A identifiers left gap-marked
[CC verifies at Step 0 pre-flight item 9] — exact InductionOutcome / induct_seed return shape against src/loomworks/engagement/creation.py:409 definition; the response body adopts the verbatim downstream shape.
1. Auth: resolve actor via Depends(get_resolved_actor) per existing convention
(matches Phase 16 upload endpoint at files.py:140 and engagement endpoints).
Confirm actor has the right designation to invoke seed extraction on this
engagement [CC verifies designation requirement at Step 0 pre-flight item 8
against /seed/induct's auth posture].
2. Load UploadedFileRow by file_id from the request body. Confirm row exists
and belongs to the path-parameter engagement_id (file_id-engagement_id
binding check per Phase 16 convention). 404 if not found; 403 if cross-
engagement.
3. Read the file path from row.storage_path. Confirm content_type ==
"text/markdown" (defensive against future _ACCEPTED extension to other
text types; Phase 53 scope is markdown only per P53-D6). 415 if not
markdown.
4. Invoke the skill directly:
result = await extract_discovery_to_seed(
file_path=Path(row.storage_path),
engagement_id=engagement_id,
db=db,
secret_key=None, # triggers system-level key resolution per §8.7
person_id=actor.person_id,
)
The skill returns ExtractionResult; the structured payload lives in
result.extra_metadata["draft_seed"].
5. Extract the draft_seed payload:
draft_seed_payload = result.extra_metadata["draft_seed"]
gap_fields = result.extra_metadata.get("gap_fields", [])
6. Fill R-A10 fields at hand-off (R-A10 is endpoint-derived, not LLM-extracted
per §8.6):
draft_seed_payload["initial_contributors"] = [actor.person_id]
draft_seed_payload["initial_agents"] = <default registered agents>
[CC verifies at Step 0 pre-flight item 9] — default-agent-resolution
helper used by Phase 25's commit path.
7. Write the discovery_to_seed_extracted Memory event:
await append_event(
db=db,
engagement_id=engagement_id,
event_kind="discovery_to_seed_extracted",
payload={
"discovery_document_ref": str(file_id),
"discovery_document_filename": row.original_filename,
"extracted_seed_payload": draft_seed_payload,
"skill_version": "1.0",
"prompt_version": PROMPT_VERSION,
"extraction_model": EXTRACTION_MODEL,
"gap_fields": gap_fields,
"extracted_at": <now()>,
},
actor=actor,
)
FORAY hooks fire automatically per §10.8.
8. Construct the Seed and call induct_seed (becomes the third production
caller per V5):
seed = await draft_seed(
db=db,
engagement_id=engagement_id,
seed_fields=draft_seed_payload,
actor=actor,
)
outcome = await induct_seed(
db=db,
engagement_id=engagement_id,
seed_id=seed.id,
actor=actor,
)
[CC verifies at Step 0 pre-flight item 9] — exact draft_seed / induct_seed
signatures and call shapes from the two existing production sites
(seed_amendment.py:133 and engagements.py:361).
9. Construct ExtractSeedResponse and return 200.
Exact shape: [CC verifies at Step 0 pre-flight items 7, 8, 9, 10]. The implementation lands as a single route handler in engagements.py (or a new seed_extraction.py module); no Phase 25 / Phase 31 code is restructured.
Five failure modes:
file_id not found in UploadedFileRow. 404 Not Found. Standard FastAPI dep / lookup pattern.file_id exists but belongs to a different engagement. 403 Forbidden. Cross-engagement access denial.content_type != "text/markdown". 415 Unsupported Media Type. Defensive; Phase 53 scope is markdown only.discovery_to_seed_extracted Memory event is NOT written (no partial-success state); induct_seed is NOT called. The Operator can retry by POSTing again; per P53-D1's no-retry-on-clarification, the skill itself does not auto-retry — the Operator decides.induct_seed raises (transaction error, state-invalid, etc.). Halt-and-surface at runtime per the same convention as the other two production callers. The discovery_to_seed_extracted Memory event IS written (step 7 completed before step 8); the induction outcome is recorded as a failure. The Operator can retry induct_seed via the existing /engagements/{eid}/seed/induct endpoint or via amendment. Phase 53 alpha posture: no retry / fallback inside the endpoint.Phase 53 scope: single-document, single-call, synchronous endpoint. Batch-extract (multiple documents → single seed), async / Celery / background-task variants, and streaming-LLM-response variants are all out of scope. Phase 54+ candidates if needed.
The endpoint is purely additive — no existing route signatures change; no existing route handlers are touched. Phase 25's five /engagements/{eid}/seed* routes (V10 enumeration: lines 215, 297, 343, 425, 822 in engagements.py + the route in seed_view.py at 34) continue to function identically. Phase 31's five seed_conversation.py routes (V5 enumeration: lines 491, 813, 857, 877, 938) continue to function identically.
induct_seed's two existing production callers (seed_amendment.py:133, engagements.py:361 per V5) are unchanged; the third caller (this endpoint) joins them without disturbing the function signature.
§17 includes explicit backward-compat smoke tests for Phase 25 (form-submission path produces identical output and identical induct_seed invocation) and Phase 31 (conversational path produces identical string-brief output, identical five engagement-scoped endpoint shapes, and continues to NOT call induct_seed — the V5 gap is preserved deliberately per P53-D11).
Empty by default per P53-D4.
The Operator Layer's existing Phase 31 conversational creation flow remains unchanged (V5 confirmed the five engagement-scoped endpoints intact). Phase 54+ adds the Discovery-assistance surface once queued directions §1.1 design lands.
This section exists for symmetry with Phase 52's §12 frontend section and to mark the absence explicitly; it is not a placeholder for unbuilt work.
Operator Layer baseline at Phase 53 close: 139 vitest passed, 11 prerendered routes, eslint/tsc/build clean. Marker tag phase-51-marketing-site-and-companion-email at frontend e4c09e0 remains in place; Phase 53 does not advance a marker.
Empty. Phase 53 doesn't touch the marketing repo.
The marketing site (DUNIN7/loomworks-marketing, live at https://loomworks.doneinseven.com per Phase 51 / 52 close) is unrelated to engagement creation flow — its form is the public grant-request entry point for the credit / form-flow / marketing arc, not the engagement creation arc Phase 53 opens.
This section exists for symmetry with Phase 52's §13 marketing-site section and to mark the absence explicitly; it is not a placeholder for unbuilt work.
Seven slots: 4 active + 3 reserved buffer per scoping v0.2 §7. Two checkpoints (A and B). Inverted-pyramid ordering: smallest independent slice first (Phase 16 extension); skill module + registration next; orchestration on top; full test sweep last.
_ACCEPTED MIME-type extension (engine)
What. The smallest independent change: extend _ACCEPTED at src/loomworks/api/routers/files.py:102 to include text/markdown per §7 / P53-D6 / V6. Land green against a single smoke test confirming text/markdown upload succeeds.
Step 0 inspection. Before implementing, CC reads:
_ACCEPTED exact line and adjacent test coverage.
Build. Per §7. Choose option (i) (named _ACCEPTED_MARKDOWN constant) or option (ii) (inline literal) per local convention. Add the constant / inline literal; update _ACCEPTED union.
Tests. 1–2 tests:
POST /engagements/{eid}/files with content_type="text/markdown" and a small Markdown body returns 201 and produces an UploadedFileRow with content_type == "text/markdown".POST /engagements/{eid}/files with content_type="text/csv" still 415-rejects (regression check).Acceptance gate. Step 1 tests pass; existing Phase 16 reject-behavior tests pass unchanged.
Halt threshold. Per §16 step-specific: Phase 16 _ACCEPTED extension surfaces unexpected interactions with existing MIME-type filters (e.g., the union expression has cascade effects through a security validator that depends on the closed list; or downstream code in contributions.py:97 branches on specific known MIME types). >30 tests touched.
Mode posture. Auto-mode-proceed.
What. The skill substrate. New skill module per §8; new prompt template per §8.8; registration in build_default_registry() per §9. Tests run against mocked LLM responses (per drafting handoff §5 LLM-mocking discipline).
Step 0 inspection. Before implementing, CC reads:
src/loomworks/skills/ directory.src/loomworks/prompts/creation_conversation.py (V5 evidence: PROMPT_VERSION, CONVERSATION_MODEL, prompt literal, __all__ exports).build_default_registry() at registry.py:165–195.src/loomworks/skills/registry.py:64–90 (ExtractionSkill Protocol; for signature conformance).src/loomworks/skills/registry.py:28–61 (ExtractionResult dataclass; for return shape).src/loomworks/skills/image_description.py or transcription.py (one of the existing LLM-using skills; for LLM-client pattern).Seed model field declarations (location per V4 enumeration; for prompt-target field names).
Build. Per §8 (skill module body) + §8.8 (prompt template) + §9 (registration). Skill conforms to ExtractionSkill Protocol; UTF-8 decoding internal; single-shot LLM call; structured-output parsing; ExtractionResult return with draft_seed in extra_metadata. Prompt template module mirrors Phase 31's creation_conversation.py shape.
Tests. ~6–10 tests:
build_default_registry().resolve("text/markdown") returns a RegisteredSkill with skill == extract_discovery_to_seed, label, and mode per §9.3.extract_discovery_to_seed is callable with the ExtractionSkill Protocol signature; passes static type checks.loomworks-marketing-creation-flow-content-v0_3.md per V8 evidence. Mock LLM returns a structured payload covering R-A5–R-A9 + R-A11; skill returns ExtractionResult with extra_metadata["draft_seed"] containing the expected fields; gap_fields == [].loomworks-person-layer-discovery-v0_2.md per V8 (decision-list shape). Mock LLM returns payload with some fields gap-marked; skill returns ExtractionResult with gap_fields listing the missing R-A identifiers.discovery-memory-capture-v0_1.md per V8 (Discovery-trajectory shape). Same posture as 2d; different gap pattern.discovery_document_text passed to the prompt.secret_key autouse-monkeypatch: per V1 evidence, per-module convention. Test module's autouse fixture monkeypatches settings.loomworks_secret_key to TEST_SECRET_KEY. Skill's secret_key=None path triggers system-level key fetch from system_config (mocked / fixture-injected for tests).key_source populates correctly: ExtractionResult.key_source == "system" when skill resolves via system-level key path.PROMPT_VERSION, EXTRACTION_MODEL, EXTRACTION_PROMPT import cleanly from src/loomworks/prompts/discovery_to_seed_extraction.py.Acceptance gate. All Step 2 tests pass; registration present; skill conformant; full-fit and 2+ partial-fit fixtures extract per expected payloads; gap-marker behavior verified; hallucination-resistance verified.
Halt threshold. Per §16 step-specific: ExtractionSkill Protocol surface differs from V1's read (e.g., the secret_key kwarg is positional not keyword; the return is not ExtractionResult but a different dataclass; the protocol requires a method the skill can't naturally provide). LLM client / key resolution convention doesn't match Phase 31's system_config pattern. Prompt template structural convention differs from Phase 31's creation_conversation.py enough that __all__ exports don't fit.
Mode posture. Auto-mode-proceed.
CC produces implementation notes draft at /Users/dunin7/loomworks-engine/docs/phase-impl-notes/phase-53-implementation-notes-v0_1.md. Records build summary across Steps 1–2; any in-flight resolutions (naming-only divergences absorbed); test count delta. Operator confirms before Step 3.
Acceptance gate (Checkpoint A).
induct_seed hand-off (engine)
What. Wire the skill substrate into the endpoint orchestration. New discovery_to_seed_extracted Memory event class per §10; _ANCHOR_PRIORITY entry per §10.4 / P53-D12; new POST /engagements/{eid}/seed/extract endpoint per §11.
Step 0 inspection. Before implementing, CC reads:
_ANCHOR_PRIORITY insertion point at events.py:112–130; seed-family proximity for ordering./seed/extract route placement against engagements.py vs new module.induct_seed exact signature and call shape from seed_amendment.py:133 and engagements.py:361.compute_content_hash exact import and _foray injection alignment per V9.src/loomworks/engagement/creation.py:409 (induct_seed definition).src/loomworks/engagement/creation.py (draft_seed definition; for seed construction shape at endpoint step 8).src/loomworks/credit/jurisdiction_routing.py:42–62 (Phase 52 typed-MemoryObject class template per V7; for class-body shape; Phase 53 mirrors structurally but adds _ANCHOR_PRIORITY entry where Phase 52 declined to).seed_drafted / seed_amended class declaration sites for the FORAY-anchored seed-family pattern (P53-D12 mirror target).
Build. Per §10 (Memory event class + _ANCHOR_PRIORITY entry) + §11 (endpoint orchestration). The Memory event class lands as a new file src/loomworks/engagement/discovery_to_seed.py (per pre-flight item 7); the _ANCHOR_PRIORITY entry adds at events.py:112–130; the endpoint extends engagements.py (per pre-flight item 8 default).
Tests. ~6–10 tests:
DiscoveryToSeedExtracted instantiates cleanly with valid payload; object_type matches Literal; field validation works (datetime tz, etc.)._ANCHOR_PRIORITY entry: "discovery_to_seed_extracted" in _ANCHOR_PRIORITY; value == "standard"._foray namespace injection: appending a discovery_to_seed_extracted event produces a payload with _foray block populated per V9 mechanics. Mirrors tests/test_phase_25_content_hash_and_foray.py structure./engagements/{eid}/seed/extract with a valid file_id referencing a text/markdown upload returns 200 with ExtractSeedResponse shape; the response's extracted_seed_payload matches the mocked-LLM-returned payload; seed_id and induction_outcome populate.file_id belonging to a different engagement returns 403.file_id referencing a non-markdown upload (e.g., an image) returns 415.discovery_to_seed_extracted event appears in the engagement's event log with the correct schema (per §10.3 fields).induct_seed hand-off: after a successful endpoint call, the seed is inducted (a seed_drafted event appears followed by induction-cycle events per Phase 25 canonical path). Mock can stub induct_seed itself or run the real one if test fixtures allow.initial_contributors == [actor.person_id]; initial_agents == <default agents>.discovery_to_seed_extracted event written; no induct_seed called.
Acceptance gate. All Step 3 tests pass; Memory event class registers cleanly; _ANCHOR_PRIORITY entry present and produces _foray injection; endpoint orchestrates the five-step flow correctly; induct_seed becomes the third production caller; R-A10 endpoint-fill works.
Halt threshold. Per §16 step-specific: induct_seed hand-off contract differs from V5's implication (e.g., requires Phase 31-style string brief rather than structured Seed; requires async context the endpoint can't provide). _ANCHOR_PRIORITY insertion produces cascade effects (e.g., existing tests that assert specific _ANCHOR_PRIORITY contents now fail). New Memory event module placement conflicts with existing imports / circular dependencies.
Mode posture. Auto-mode-proceed.
What. Full fixture and integration test sweep. Backward-compat smoke tests for Phase 25 and Phase 31. Per scoping v0.2 §3.7.
Step 0 inspection. Before implementing, CC reads:
tests/test_phase_25_*.py) for the backward-compat fixture shape.tests/test_phase_48_seed_conversation_routing.py per V1 evidence enumeration; plus any other test_phase_31_ or test_seed_conversation_ files) for the Phase 31 backward-compat shape.tests/test_phase_25_content_hash_and_foray.py (V9 canonical fixture; for the Phase 53 FORAY-coverage test pattern)./Users/dunin7/loomworks-engine/docs/discovery-records/ and /Users/dunin7/Downloads/ (or wherever the engine-repo's local copies land; CC selects 3 fixtures: full-fit + 2 partial-fit).
Build. New test file tests/test_phase_53_discovery_to_seed.py. Land the broader integration and backward-compat tests not covered in Steps 1–3.
Tests. ~7–10 tests:
loomworks-marketing-creation-flow-content-v0_3.md via POST /engagements/{eid}/files; then POST /engagements/{eid}/seed/extract with the returned file_id; assert the candidate seed has all R-A5–R-A9 + R-A11 fields populated; gap_fields == []; induction loop runs to completion (or to first-cycle findings per Phase 25 canonical path).gap_fields includes the expected missing R-A identifiers; induction loop produces findings for the gap-marked fields./seed/extract call, compute_content_hash(payload) on the read-back discovery_to_seed_extracted event matches the stored content_hash column; _foray block is present in the payload with anchor_priority == "standard", anchorable == True. Mirrors tests/test_phase_25_content_hash_and_foray.py shape.POST /engagements → POST /engagements/{eid}/seed/induct form-submission path produces identical Seed shape and identical induct_seed invocation. No regression.induct_seed continues to have 2 existing production callers (seed_amendment.py:133 and engagements.py:361); Phase 53 adds a third (the new endpoint) but doesn't replace either. Static / grep check or test-discovery confirmation.induct_seed (the V5 gap is preserved deliberately per P53-D11). Static / grep check._ACCEPTED MIME types continue to upload successfully; the 415-reject still fires for non-_ACCEPTED types (e.g., text/csv).Acceptance gate. All Step 4 tests pass; total test count delta within ~20–30 over Phase 52 baseline (target: 2,157–2,167 substrate tests); Phase 25, Phase 31, Phase 16 backward-compat smoke green.
Halt threshold. Per §16 step-specific: any backward-compat regression on Phase 25 (form-submission path produces different output; induct_seed invocation changes) or Phase 31 (any of the five endpoints fails; string-brief output changes; Phase 31 now calls induct_seed despite P53-D11 saying gap is preserved) or Phase 16 (existing MIME types no longer upload; _ACCEPTED reject behavior regresses). >30 tests touched by a single change.
Mode posture. Auto-mode-proceed.
CC produces implementation notes v0.2 (or higher if amendments consumed reserved slots) at /Users/dunin7/loomworks-engine/docs/phase-impl-notes/phase-53-implementation-notes-v0_2.md. Records build summary across all Steps; any in-flight resolutions; methodology findings (lens-bounded scoping evidence cascade second-instance evidence per scoping v0.2 §10.3 — V4/V5/V10 enumeration discrepancies; bounded-contract-not-no-LLM crystallization; engagement-scoped URL family universality; Phase 16 additive MIME extension precedent; P53-D12 settlement of v0.2 FORAY contradiction).
Acceptance gate (Checkpoint B). Per §15.
Tag. phase-53-discovery-to-seed-skill on DUNIN7/loomworks-engine at the post-Step-4 engine commit. Annotated tag. Pushed to origin.
No tags on Operator Layer or marketing repos by default per P53-D7. If sub-arc 2 lands actual work via mid-build amendment, marker tag follows Phase 51 V7 precedent.
(reserved — buffer for amendments arising from steps 1–4)
Unconsumed if no amendment arises. Phase 50 left all three reserved slots unconsumed; Phase 51 left all three reserved slots unconsumed; Phase 52 left all three reserved slots unconsumed per the Phase 52 §14 carry. Phase 53 expects similar posture given Step 0 absorbed ten verifications including the one BREAKS (V6) reshaped at v0.2 and the FORAY contradiction settled at this CR by P53-D12.
(reserved — buffer)
(reserved — buffer)
Twelve items per scoping v0.2 §8. Mirrors the Phase 52 v0.1 8-item shape, scaled up for Phase 53's larger scope (Phase 16 amendment + skill module + prompt template + Memory event + _ANCHOR_PRIORITY entry + endpoint + tests).
| # | Gate | Where verified |
|---|------|---------------|
| 1 | All Step 1 tests pass; Phase 16 _ACCEPTED extended to include text/markdown; Markdown upload succeeds with 201; existing MIME types continue to work; existing 415-reject behavior preserved for non-_ACCEPTED types. | Step 1 |
| 2 | All Step 2 tests pass; skill module exists at src/loomworks/skills/discovery_to_seed.py; conforms to ExtractionSkill Protocol per V1; registered as fourth skill in build_default_registry(). | Step 2 |
| 3 | All Step 3 tests pass; DiscoveryToSeedExtracted MemoryObject class registers cleanly; _ANCHOR_PRIORITY entry present and produces _foray injection per P53-D12 / V9. | Step 3 |
| 4 | All Step 4 tests pass; full-fit and 2+ partial-fit fixtures extract per expected payloads. | Step 4 |
| 5 | Endpoint POST /engagements/{eid}/seed/extract orchestrates the five-step flow correctly: file load → skill invocation → R-A10 fill → Memory event write with FORAY → induct_seed hand-off → response. | Step 3 / cross-cutting |
| 6 | Candidate Seed reaches induct_seed per Phase 25 canonical path; induction loop runs unchanged. The endpoint becomes the third production caller alongside seed_amendment.py:133 and engagements.py:361. | Step 3 / cross-cutting |
| 7 | discovery_to_seed_extracted Memory event written with full FORAY namespace per P53-D12 / V9: _foray block present with anchor_priority == "standard", anchorable == True, content_hash matching computed hash; content_hash column populated. | Step 3 / Step 4 cross-cutting |
| 8 | Skill produces Seeds from at least three project-knowledge Discovery records (full-fit + 2 partial-fit fixtures per V8); hallucination-resistance test passes (skill leaves fields gap-marked rather than fabricating). | Step 2 / Step 4 cross-cutting |
| 9 | Phase 25 form-submission path unchanged (backward-compat verified): existing POST /engagements → POST /engagements/{eid}/seed/induct produces identical Seed shape; induct_seed invocation identical; both existing production callers preserved. | Step 4 / cross-cutting |
| 10 | Phase 31 conversational-creation path unchanged (backward-compat verified): five engagement-scoped endpoints per V5 function identically; string-brief output unchanged; Phase 31 continues to NOT call induct_seed (the V5 gap is preserved deliberately per P53-D11). | Step 4 / cross-cutting |
| 11 | Lint + tsc + build clean on Operator Layer (sub-arc 2 empty per P53-D4); no changes to Operator Layer repo. | Cross-cutting |
| 12 | Working tree clean on main for engine repo before tag; tag phase-53-discovery-to-seed-skill lands as annotated tag at post-Step-4 commit; pushed to origin. Operator Layer and marketing repos: untouched (no tags, no commits per P53-D7 default). | Checkpoint B |
Per scoping v0.2 §9 and Phase 49 / 50 / 51 / 52 substrate-friction-discipline-pattern. Mid-build friction at any of these thresholds halts the build and surfaces for Operator-elective amendment scoping. Phase 53 thresholds are tightened against Step 0 findings absorbed in scoping v0.2 plus the FORAY question settled at this CR by P53-D12.
General (any step).
induct_seed production callers; V10: 10 /seed* routes across 3 modules). Third-instance lens-bounded scoping evidence cascade would itself be methodology contribution worth preserving.Step-specific.
_ACCEPTED extension produces unexpected interactions with existing MIME-type filters (e.g., the MIME-type filter is referenced from multiple modules; there's a security validator that depends on the closed list; downstream code in contributions.py:97 branches on specific known MIME types in ways the new text/markdown member surfaces). >30 tests touched.secret_key kwarg is positional not keyword; the return type is not ExtractionResult). LLM client / key resolution convention doesn't match Phase 31's system_config pattern (per pre-flight item 11). Prompt template structural convention differs from Phase 31's creation_conversation.py enough that __all__ exports don't fit. Discovery-record fixtures don't actually exist at the V8-enumerated paths (the engine-repo docs/discovery-records/ directory is empty, or the V8-named files have moved / been renamed).induct_seed signature contradicts V5's implication (e.g., requires a Phase 31-style string brief rather than a structured Seed; requires async context the endpoint can't provide; requires transaction-context ceremony the endpoint location doesn't have). _ANCHOR_PRIORITY insertion produces cascade effects (e.g., existing tests that assert specific _ANCHOR_PRIORITY contents now fail because they hardcoded the 14-entry list; or downstream code that iterates _ANCHOR_PRIORITY now has unexpected behavior). New Memory event module placement at src/loomworks/engagement/discovery_to_seed.py conflicts with existing imports / circular dependencies. New endpoint placement at engagements.py doesn't fit cleanly (e.g., the module's auth posture or transaction-context wrapping is incompatible with the new orchestration shape).induct_seed invocation changes; either existing production caller is broken) or Phase 31 (any of the five engagement-scoped endpoints fails; string-brief output changes; the V5 gap is closed despite P53-D11 saying it's preserved) or Phase 16 (existing 18 MIME types no longer upload; _ACCEPTED reject behavior regresses for non-allowed types). Discovery fixtures produce wildly variable extraction outcomes such that the gap-marker behavior can't be tested reliably (LLM-mocking discipline failing at the fixture level — Phase 54+ candidate for live-LLM cadence becomes Phase 53 blocker).In all cases, halt-and-surface is preferred to draft-and-hope. Reserved buffer slots 5–7 absorb amendment scoping if needed; if no amendment arises, slots stay unconsumed per Phase 50 / 51 / 52 precedent.
Total substrate: ~20–30 new tests across Steps 1–4. Operator Layer: 0 by default per P53-D4. Marketing site: 0.
| Step | Substrate | Frontend (Operator Layer) | Marketing |
|------|-----------|---------------------------|-----------|
| Step 1 | 1–2 (Phase 16 extension smoke) | — | — |
| Step 2 | 6–10 (skill + prompt template + registration; fixtures) | — | — |
| Step 3 | 6–10 (Memory event + endpoint + FORAY + induct_seed hand-off) | — | — |
| Step 4 | 7–10 (integration + backward-compat smoke) | — | — |
| Total | ~20–30 | 0 | 0 |
Build time estimate: 2–2.5 hours total per scoping v0.2 §7. Test count delta target: ~20–30 over the Phase 52 close baseline of 2,137.
The tests follow the green-against-mocked-LLM posture across Steps 2–3 (LLM responses mocked at the fixture level per drafting handoff §5 LLM-mocking discipline); Step 4 exercises the integration path against real Phase 16 + Phase 25 substrate with mocked LLM responses for the extraction step.
LLM-mocking discipline carries forward as Phase 54+ candidate: a separate non-CI test (or quarterly verification cadence) can run against live LLM to confirm prompt-extraction quality on the V8 fixtures. Phase 53 ships with mocked LLM only; the live-LLM cadence is opportunistic Phase 54+ work.
Compared to Phase 52's ~6–9 substrate / 0 frontend, Phase 53 is ~3–5x larger in test surface — proportionate to the substrate scope (4 substantive surfaces vs Phase 52's 4 narrower surfaces) and the integration-heavy nature of LLM-assisted skill testing.
Items intentionally not built in Phase 53, recorded for Phase 54 scoping. Mirrors Phase 50 / 51 / 52 §18 explicit-list shape.
induct_seed like Phase 53). If incompleteness, Phase 31 amendment lands at Phase 54+; closing the gap by calling draft_seed + induct_seed from seed_conversation.py:468 natural extension.discovery_to_seed_extracted (deferred per P53-D9 / Phase 50/52 precedent). Phase 54+ adds when downstream consumers built that need deserialize_memory_object("discovery_to_seed_extracted", payload) — likely the Discovery-trajectory specialist (item 2) or the Operator Layer surface (item 3).loomworks_cors_origins (Phase 51 §19 item 2 / Phase 52 §18 item 5). Conditional. Unchanged carry._author_proposal_assertion (Phase 52 V2 set-aside option (b) / Phase 52 §18 item 3). Available if a future phase introduces a need; unchanged carry.<GrantDecisionApprovalCard> (Phase 51 §19 item 6 / Phase 52 §18 item 8). Opportunistic.grant_proposal.md and grant_email.md (Phase 51 §19 item 10 / Phase 52 §18 item 12). Continuing parallel work.conversation_turns reconciler coverage (Phase 51 §19 item 11 / Phase 52 §18 item 13). Substrate-gap-dependent; future Phase 42 amendment.Seed), V5 (5 endpoints not 4; engagement-scoped not system-scoped; Phase 31 doesn't call induct_seed), V10 (10 routes; uniformly engagement-scoped). Three enumeration discrepancies in the same phase, all in the same direction (scoping note reads more compact than substrate). Promotes from methodology candidate to named principle in v0.38 manifest bump./seed* routes are engagement-scoped; no system-scoped sibling family exists. Methodology absorption: engagement-scoped URLs are the canonical convention; system-scoped routes are exceptions, not parallel families._ACCEPTED extension plus consumer-side decoding; no rebuild of Phase 16's pipeline._ANCHOR_PRIORITY), the CR drafting halt-and-surface discipline catches it; Operator settles at CR review; the settlement carries forward as a new P-decision (P53-D12). Worth recording as Phase 53 implementation-notes finding for methodology v0.21 absorption.These contributions land in Phase 53 implementation notes during construction and feed the next manifest update (v0.38) / methodology consolidation pass.
Paste-ready. Mirrors the Phase 52 §19 kickoff shape, adapted for Phase 53's four-active-step / engine-only / larger-scope shape. Run on DUNIN7-M4 in a fresh Claude Code session against the engine repo at /Users/dunin7/loomworks-engine.
> Read the Change Request document at the path I supply below. This is
> CR-2026-068 v0.1, the Phase 53 Change Request (first version; no prior
> Phase 53 CR exists). You are the executing agent named in the CR.
>
> CR path: ~/Downloads/phase-53-cr-discovery-to-seed-skill-v0_1.md
> (confirm the latest approved version if more than one is present in
> Downloads).
>
> v0.1 drafts against:
> - loomworks-phase-53-scoping-note-v0_2.md (authoritative scope; absorbs
> Step 0 findings)
> - phase-53-step-0-findings-v0_1.md (engine repo
> docs/phase-impl-notes/; verified live-codebase state, absorbed into
> scoping v0.2; FORAY question left open in v0.2 settled at this CR
> by P53-D12)
> - loomworks-phase-53-cr-drafting-handoff-v0_1.md (drafting handoff)
>
> Code baseline:
> - engine: tag phase-52-jurisdiction-routing at 69aecdc (annotated tag
> object f11e41a); main HEAD = 69aecdc (the tag commit per Step 0
> findings baseline check; brief's phrasing of "one commit ahead at
> 14e5a7f" inverted parent/child — 14e5a7f is the parent of 69aecdc).
> 2,137 tests passed, 25 skipped, Alembic head 0064, working tree
> clean.
> - Operator Layer (DUNIN7/loomworks): marker tag
> phase-51-marketing-site-and-companion-email at e4c09e0 (Phase 50
> commit, sub-arc 2 has been empty across Phases 51 and 52). 139
> vitest passed, 11 prerendered routes, eslint/tsc/build clean. NOT
> IN PHASE 53 SCOPE BY DEFAULT per P53-D4.
> - Marketing (DUNIN7/loomworks-marketing): tag at bf2f694 (annotated
> tag object 76071b0); main one commit ahead at 78c26fa. Live at
> https://loomworks.doneinseven.com. NOT IN PHASE 53 SCOPE.
>
> Per CR §3.4: archive this CR to
> docs/phase-crs/phase-53-cr-discovery-to-seed-skill-v0_1.md
> at Step 0 before Step 1 begins.
>
> Per CR §3.3: run pre-flight Step 0 (11 items). The ten scoping-time
> verifications absorbed into v0.2 (V1–V10) are not re-run; the pre-flight
> items here are CR-time deltas — placement convention confirmations,
> exact strings, module paths, ordering conventions that the CR drafter
> specified against Step 0 evidence but where local convention selection
> remains. Naming-only divergences absorb in-flight per the standard
> discipline; architectural divergences halt and surface (see CR §16
> halt conditions).
>
> Per CR §14: four active build steps + three reserved buffer slots
> (Steps 5–7 reserved-not-skipped per scoping v0.2 §7 reserved-slot
> convention). Two checkpoints — Checkpoint A after Step 2, Checkpoint B
> after Step 4 (final, before tagging). Standard auto-mode posture:
> Steps 1–2 accept auto-mode-proceed; Checkpoint A halts until Operator
> confirms; Steps 3–4 auto; Checkpoint B (final) halts for tagging.
>
> Per CR §4: twelve construction decisions (P53-D1 through P53-D12) are
> settled. CC executes against them; does not re-decide. P53-D12 in
> particular settles the FORAY question scoping v0.2 left open between
> its §3.5 schema framing and §6 V7 row — Phase 53 mirrors Phase 25's
> seed-family (add discovery_to_seed_extracted to _ANCHOR_PRIORITY at
> priority "standard"), not Phase 50/52's deferral. No decisions remain
> [Operator confirms].
>
> Per CR §16: halt-and-surface is preferred to draft-and-hope. Halt
> thresholds include: substrate-friction-discipline-pattern; any
> divergence from v0.2 scoping decisions or this CR's P53-D12; >30 tests
> touched by a single change; any count discrepancy emerges against
> Step 0's enumerations (V1: 3 production registrations + 18+ test
> modules; V4: 7 production Seed-construction sites + 33 test sites; V5:
> 5 Phase 31 endpoints + 2 induct_seed production callers; V10: 10
> /seed* routes across 3 modules) — third-instance lens-bounded scoping
> evidence cascade would itself be methodology contribution worth
> preserving; ExtractionSkill Protocol surface differs from V1 read;
> induct_seed signature contradicts V5; _ANCHOR_PRIORITY insertion
> produces cascade effects; any backward-compat regression on Phase 25,
> Phase 31, or Phase 16.
>
> Implementation notes at Checkpoints A and B:
> docs/phase-impl-notes/phase-53-implementation-notes-v0_1.md (and
> v0_2.md if revised at Checkpoint B). Records build summary for each
> step, in-flight resolutions, methodology findings (lens-bounded
> scoping evidence cascade second-instance evidence per scoping v0.2
> §10.3 — V4/V5/V10 enumeration discrepancies; bounded-contract-not-
> no-LLM crystallization; engagement-scoped URL family universality;
> Phase 16 additive MIME extension precedent; P53-D12 settlement-of-
> scoping-contradiction-at-CR-drafting as new methodology candidate).
>
> Tag at completion: phase-53-discovery-to-seed-skill
> (annotated, on engine repo only per P53-D7). No tags on Operator
> Layer or marketing repos by default — Phase 53 has no work in those
> repos. If sub-arc 2 lands actual work via mid-build amendment,
> marker tag follows Phase 51 V7 precedent.
> Push tag after Checkpoint B.
After CC reports build summary at completion, a fresh scoping chat opens for Phase 54 with the carry-forward from §18 as relevant.
If a mid-build amendment surfaces (Phase 49 Finding 6 trajectory — Operator-elective amendment scoping), the discipline is established: build doesn't halt on naming-only items; Operator decides architecturally with options + halt-threshold review; sub-step lands before continuing. Phase 49's phase-49-step-4-amendment-scoping-v0_1.md is the canonical instance; Phase 50 / 51 / 52 each left all three reserved slots unconsumed. Phase 53's most likely mid-build amendment triggers are LLM-mocking-discipline failure at Step 2's fixture level (Discovery records produce wildly variable outcomes the mock can't deterministically represent) and any Step 3 backward-compat surface change (_ANCHOR_PRIORITY cascade effects on existing tests; new endpoint placement conflict).
DUNIN7 — Done In Seven LLC — Miami, Florida Phase 53: Discovery-to-Seed Skill — CR v0.1 — 2026-05-10