ID-75 — PullMD remote URL/feed source: PLAN ({75.4})
ID-75 — PullMD remote URL/feed source: PLAN ({75.4})
Section titled “ID-75 — PullMD remote URL/feed source: PLAN ({75.4})”{75.4} PLAN artefact. Decomposes the ratified {75.2} PRODUCT (BI-1..BI-23) +
{75.3} TECH (WP-A..WP-G, D-1..D-12, M1/M2, §5 landing-set contract, §6.1 spine) into
implementation Subtasks {75.5}..{75.15}. Authored by a fresh Planner dispatch
(Q-PLANNER-2); both predecessor specs read in full.
- Task: ID-75 “Wire PullMD into cocoindex correctly — remote URL/feed source” (bl-217).
- Ratifications inherited: TECH ratified by Liam S318 — OQ-T1 (CV 13 re-homing per
§6.3), OQ-T2 (poller-fired
POST /walknudge + hourly fallback;CRON_SECRETreuse + net-newCOCOINDEX_WORKER_URL), OQ-T3 (ingestion_sourcespelling). - Scope: Slice 1 (landing) only — BI-13/14/15 are ID-58/ID-71 seats, deliberately NOT decomposed here.
- Authored: S318 (06/06/2026). Language: UK English; dates DD/MM/YYYY.
1. Code-intelligence orientation (run 06/06/2026 at this HEAD)
Section titled “1. Code-intelligence orientation (run 06/06/2026 at this HEAD)”gitnexus_query({query: "feed articles url landing reference items promotion pipeline"})returned the promotion-path symbols as standalone definitions withprocesses: [](no indexed cross-module flow):Function:lib/intelligence/pipeline.ts:storeAsContentItem(:576-734),Function:lib/intelligence/pipeline.ts:processFeedSource(:276-536),Function:lib/intelligence/pipeline.ts:isDuplicate(:216-254), plus the untouched consumer surfaces (articles/rssroutes) BI-12 protects.gitnexus_context({name: "processFeedSource"})— incoming calls: exactly 1 (runPipeline);processes: []. Verdict LOW structural / HIGH product significance — matches TECH §1.1 verbatim; the {75.13} brief carries the full impact-before-edit discipline regardless.- Python-side grounding is grep-based per
.ast-dataflow/CLAUDE.md(ts-morph does not cover Python). Pre-verified for the WP-E wrinkle:inferContentTypeis exported atpipeline.ts:165, sole production caller at:704(insidestoreAsContentItem), and imported by exactly two test files (__tests__/lib/intelligence/content-type-update.test.ts,__tests__/lib/intelligence/si-gap-fixes.test.ts) — carried into the {75.13} brief. - Types-regen convention verified: the CI
supabase-types-parityjob generates from staging ($STAGING_PROJECT_REF,.github/workflows/ci.yml:1062-1069); the prod project-id in its error-hint string is stale. The {75.5} brief instructs staging-side regeneration accordingly.
2. Decomposition (11 Subtasks, {75.5}–{75.15})
Section titled “2. Decomposition (11 Subtasks, {75.5}–{75.15})”| ID | Title (short) | WP / decisions | Deps (sibling-only) |
|---|---|---|---|
| 75.5 | M1 migration — reference layer | WP-A | — |
| 75.6 | M2 migration — retrieval RPCs | WP-B | 5 |
| 75.7 | URL normalise + SSRF leaf modules + parity fixture | WP-C (D-8) | — |
| 75.8 | FeedUrlSource + UrlItem + EXECUTOR-VERIFY-1 | WP-C (D-1/D-2/D-4) | 7 |
| 75.9 | adapters.py — epoch-keyed fetch + HTML retirement | WP-C/WP-D (D-4) | — |
| 75.10 | flow.py — schemas + ingest_url component | WP-C steps 1–8 (D-6/D-7/D-9/D-10/D-12) | 5, 8, 9 |
| 75.11 | app_main wiring + containment (+ WP-G docstring) | WP-C/WP-G | 10 |
| 75.12 | Cadence ops — Vercel env + Coolify hourly fallback | D-3 (OQ-T2) | 11 |
| 75.13 | TS promotion retirement + walk nudge | WP-E (D-3/D-5) | 11 (+ orchestrator gate, §4) |
| 75.14 | Ontology register entry + doc edits | WP-F | 5 |
| 75.15 | Prod cutover (Liam-gated) | §6.1 step 6 | 6, 12, 13, 14 |
Ceiling check: 11 of 25 — comfortably within the soft cap. Full briefs (the details
fields) live in the Subtask records returned to the Orchestrator for
ledger-cli add-subtasks 75.
3. Wave diagram
Section titled “3. Wave diagram”Wave 1 (parallel — disjoint file sets): 75.5 (supabase/migrations + database.types.ts) 75.7 (url_normalise.py, url_validation.py, parity fixture + tests) 75.9 (adapters.py + adapter tests + write-path HTML fixtures)
Wave 2 (parallel): 75.6 ← 5 (migrations dir + types — serialised behind 5) 75.8 ← 7 (url_source.py + tests; FIRST STEP = EXECUTOR-VERIFY-1) 75.14 ← 5 (docs/ontology/* — documents the LANDED M1 shape)
Wave 3 (serial): 75.10 ← 5, 8, 9 (flow.py component; shares the write-path test file with 9, consumes 8's memo verdict + 9's _pullmd_fetch)
Wave 4 (serial): 75.11 ← 10 (flow.py app_main; same file as 10 ⇒ serial)
[deploy via onprem-deploy.yml → {62.10} §5 proof becomes runnable (ID-62, task-level)]
Wave 5 (parallel): 75.12 ← 11 (ops/env/runbook — no code files) 75.13 ← 11 + ORCHESTRATOR GATE: {62.10} proof green (cross-TASK, not a subtask dep)
Wave 6: 75.15 ← 6, 12, 13, 14 + HUMAN GATE: Liam prod ratificationWhy this shape:
- M1 before M2 (75.6 ← 75.5): the RPCs reference the table; both CLI-only, staging-first.
- 75.5 / 75.7 / 75.9 are genuinely parallel-safe — fully disjoint file sets. 75.9 (adapters + HTML retirement) has no dependency on the URL substrate; WP-D is unconditional.
- 75.10 is the convergence point: it needs the schema contract (5), the source +
EXECUTOR-VERIFY-1 signature verdict (8), and
_pullmd_fetch(9); it also edits the same write-path test file as 9, so serialisation avoids worktree conflicts. - 75.11 after 75.10: same file (
flow.py); the component must exist before the binding site mounts it. - Proof gates retirement (TECH §6.1 steps 3–4): 75.13 must not land before the §5
landing-set proof is green, or passed articles land nowhere in the gap. The proof is
{62.10}— ID-62’s Subtask — so the gate is task-level orchestration, restated in 75.13’sdetails, never a cross-Task subtask dependency. - 75.15 is Liam-gated (prod push + prod env), depending on the full staged slice including docs (75.14) so ratification reviews a complete picture.
4. Cross-task coordination (no constraint bends)
Section titled “4. Cross-task coordination (no constraint bends)”- {62.10} (ID-62) consumes TECH §5. Runnable once 75.5 + 75.7–75.11 are deployed to
the staging worker; explicitly does NOT require 75.13 (its green run gates 75.13). A
re-shaped
detailstext for {62.10} was returned to the Orchestrator alongside this PLAN (ID-62’s ledger record is the Orchestrator’s to edit, not this Planner’s). - {42.11}/{42.12} reconcile flags (BI-23) restated in 75.13: the manual-URL path
(
app/api/ingest/url/route.ts) is untouched here. - ID-45/T7: consumes the landing model; no data movement in ID-75 (§6.2).
5. Risks carried into briefs
Section titled “5. Risks carried into briefs”- EXECUTOR-VERIFY-1 (memo over frozen dataclass) — first step of 75.8 with the
documented scalar fallback; verdict fixes 75.10’s
ingest_urlsignature. - Retirement gap — handled by the wave-5 gate above.
- Idle-mode gate covers both sources (TECH §8) — accepted v1; 75.11 brief forbids splitting the gate.
- Supabase link drift — every migration-touching brief (75.5/75.6/75.15) opens with the verify-and-relink discipline; worktree first action is the staging link.
Changelog
Section titled “Changelog”| Date | Session | Note |
|---|---|---|
| 06/06/2026 | S318 | {75.4} PLAN authored under the ratified PRODUCT+TECH pair by a fresh Planner. 11 Subtasks ({75.5}–{75.15}), sibling-only deps verified, EXECUTOR-VERIFY-1 surfaced as the explicit first step of {75.8}, {62.10} re-shape rider returned to the Orchestrator as text. |