P4.2c — Remove correction auto-ingest (flag + direct-ingest branch) + accept ADR-037 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Execute the expand-contract contract step of ADR-037 Decision 3: now that the human-approved proposal flow has shipped (P4.2a producer + P4.2b approval UI), delete the legacy correction_auto_ingest_enabled flag and the entire correction-refinement.processor.ts direct-KB-ingest pipeline it gated, and flip ADR-037 to Accepted.
Architecture: The processor's ONLY job is direct-ingest, so removing "the direct-ingest branch" means removing the whole processor + its BullMQ queue + hourly tick + metrics enrolment. P4.2a's LoopProposalProducerService already owns the same corrections.processed_at IS NULL backlog (as human-approved proposals), so processed_at semantics cleanly converge to "attempted to turn into a proposal". Backend + docs only; zero frontend change (the flag was never rendered in the Ops feature-flag UI).
Tech Stack: NestJS 11, TypeORM 0.3, BullMQ, Jest, ADR markdown.
Context the implementer needs (verified against real code @ 8c2d693d)
ADR-037 Decision 3 (the contract): "The flag AND the processor's direct-ingest branch are removed once the human-approved proposal flow (master plan P4.2) ships; P4.2's consumer takes over the unprocessed backlog." P4.2a (LoopProposalProducerService) + P4.2b (approval UI) shipped → this is that removal.
Critical distinction: api/src/haystack/jobs/correction-refinement.processor.ts (LEGACY, being removed) ≠ api/src/config-assets/loop-proposal-producer.service.ts (P4.2a, the NEW pipeline, DO NOT TOUCH). Both read corrections WHERE processed_at IS NULL; the new one uses its own distilCorrectionToGolden, NOT the legacy categorize. Removing the legacy one does not affect P4.2a.
Verified deletion scope:
- Flag
correction_auto_ingest_enabled: definedapi/src/feature-flags/feature-flags.constants.ts:45-50(FLAG_KEYS + comment) +:106-107(DEFAULT_FLAG_VALUES + comment). Golden assertionapi/src/feature-flags/feature-flag.service.spec.ts:53(one line in a fullexpect(FLAG_KEYS).toEqual([...])). Frontend: NONE —frontend/.../feature-flags/page.tsxFLAG_KEYS (6 keys) +api.tsFeatureFlagKeyunion never listed it. Zero frontend change. - Processor
correction-refinement.processor.ts(631 lines): one linear chainprocess()→_processInContext()→processOneCorrection(), terminal = direct-ingest. No hidden second responsibility (correctionTypeis only passed through as metadata, never branched on). Integer-delete the file. - Queue/tick wiring in
api/src/haystack/haystack.module.ts: import (:24-27),registerQueue(CORRECTION_REFINEMENT_QUEUE)(:80-83), provider (:116),implements OnModuleInit+@InjectQueueconstructor (:136-140),onModuleInit()hourly tick (:142-157), and theOnModuleInitimport (:1). - Metrics
api/src/common/queue-metrics.collector.ts: constructor@Optional() @InjectQueue("correction-refinement")(:41-43) +injectedQueuesentry (:108) + a comment ref (:76). - Tests:
api/test/correction-refinement.spec.ts(808 lines, all processor behaviour) — integer-delete, BUT migrate theprocessed_at column exists / defaults NULLschema test (:620-640) — it tests entity/column semantics P4.2a also depends on, not processor behaviour.api/test/llm-callsite-metering.integration.spec.ts: import (:73) + thecategorisation meters purpose='classify'itblock (:221-255) — delete just that block + import (the file has other callsite tests that stay). - The one external symbol reference to the processor is that metering test (
Object.create(CorrectionRefinementProcessor.prototype)). All other private methods (categorize/buildRefinementDoc/hashKeyLearning/findExistingRefinement/appendToExistingDocument/normalizeCategorization) are processor-internal (thekb-seeding.service.tscategorize*are same-name-different-thing). Few-shot (correction_fewshot_enabled/learning.retrieveSimilarCorrections) shares NO symbol with the processor.
TWO Chesterton-fence checks that MUST be grep-verified during implementation (do NOT blind-delete):
haystack.module.ts:93-103LlmModule+FeatureFlagsModuleimports — a comment says they're forCorrectionRefinementProcessor. Before removing them, grep whetherGoldenAnswerSyncProcessor/ArtifactIngestionProcessor/ any other haystack provider injectsLlmService/FeatureFlagService. Remove an import ONLY if no surviving provider needs it; otherwise keep it (and drop the now-wrong comment reference).haystack.module.ts:7,75Correctionentity import +TypeOrmModule.forFeature([...Correction])— grep whether any surviving haystack service uses theCorrectionrepo. If none, it may be removed; if uncertain, leaving it is harmless (a registered-but-unused entity is not an error). Prefer safe: keep unless clearly the sole consumer was the processor.
Docs to flip (docs-only, mirror P4.1's ADR-035 accept 026695ad): ADR-037 Status + Decision 3 + Verification section; OPEN_DECISIONS.md:115 (CD-30); optional master-plan P4.2 marker; stale "correction-refinement → KB flywheel" comments in auto-correction.listener.ts:11-12 / expert-queue.service.ts:160 / entities.ts:1511 (update the downstream to "loop-proposal producer (P4.2a)"; note auto-correction.listener is the correction PRODUCER — do NOT delete it).
DECISION POINTS (reversible defaults — user may override)
- DP-1 — Integer-delete the processor, not gut-and-keep. Its sole responsibility is direct-ingest; a gutted "read backlog, do nothing" tick is pure wasted DB load (P4.2a already reads the same backlog). ADR-037 authorises removing the branch, which is the whole processor. Reversible: git history retains it.
- DP-2 — Migrate the schema test, don't drop it. The
processed_atcolumn test has standalone value (P4.2a depends on the column semantics). Moving it to a surviving spec keeps that guarantee.
Task 1: Remove the processor wiring (queue, tick, metrics) — make it unreferenced
Files:
-
Modify:
api/src/haystack/haystack.module.ts -
Modify:
api/src/common/queue-metrics.collector.ts -
Step 1: Grep the two Chesterton fences FIRST (decide before editing).
cd api
# Fence 1: does any SURVIVING haystack provider need LlmModule / FeatureFlagsModule?
grep -rn "LlmService\|FeatureFlagService\|featureFlags\|llm:" src/haystack/jobs/golden-answer-sync.processor.ts src/haystack/jobs/artifact-ingestion.processor.ts src/haystack/*.service.ts
# Fence 2: does any SURVIVING haystack file use the Correction repo?
grep -rn "Correction" src/haystack/ | grep -v correction-refinement
Record the result. If a surviving provider injects LlmService/FeatureFlagService, KEEP that module import (only drop the comment naming the processor). If none uses Correction, you may drop it from forFeature; if unsure, keep it.
-
Step 2: Edit
haystack.module.ts— remove: the{ CORRECTION_REFINEMENT_QUEUE, CorrectionRefinementProcessor }import (:24-27); theregisterQueue({ name: CORRECTION_REFINEMENT_QUEUE, ... })block (:80-83);CorrectionRefinementProcessorfromproviders(:116); theimplements OnModuleInit, the@InjectQueue(CORRECTION_REFINEMENT_QUEUE)constructor param, and the wholeonModuleInit()method (:136-157). IfonModuleInitwas the class's only reason to implementOnModuleInit, removeOnModuleInitfrom the@nestjs/commonimport (:1). Apply the Step-1 decision onLlmModule/FeatureFlagsModule/Correction. -
Step 3: Edit
queue-metrics.collector.ts— remove the@Optional() @InjectQueue("correction-refinement") ...constructor param (:41-43) and the{ name: "correction-refinement", queue: ... }entry ininjectedQueues(:108). Fix the comment at :76 that references correction-refinement (leave only surviving examples). -
Step 4: Verify it still compiles (the processor file still exists but is now unreferenced):
cd api && npx tsc --noEmit 2>&1 | tail -8
Expected: no NEW errors from these edits. (There may be an "unused" note or the still-present processor file — that's removed next task.)
- Step 5: Commit
cd /Users/daniel/Project/hp/humanwork/.worktrees/specialist-value-output-p2plus
git add api/src/haystack/haystack.module.ts api/src/common/queue-metrics.collector.ts
git commit -m "refactor(haystack): unwire correction-refinement queue/tick/metrics (P4.2c)"
Task 2: Delete the processor + migrate the schema test
Files:
-
Delete:
api/src/haystack/jobs/correction-refinement.processor.ts -
Delete:
api/test/correction-refinement.spec.ts -
Modify:
api/test/loop-proposal-producer.spec.ts(receive the migrated schema test) -
Modify:
api/test/llm-callsite-metering.integration.spec.ts -
Step 1: Read the schema test to migrate —
api/test/correction-refinement.spec.ts:620-640(theprocessed_at column exists / defaults to NULLtest). Read it verbatim so you can port its assertions. -
Step 2: Port that schema test into
api/test/loop-proposal-producer.spec.tsas a newit(or a smalldescribe("corrections.processed_at column")). It should assert thecorrections.processed_atcolumn exists and defaults to NULL, using the same datasource-build pattern the producer spec already uses (it already builds a sqlite datasource withCorrection). Keep the assertion's intent identical; adapt the setup to the producer spec's existing helpers. Run it:
cd api && npx jest test/loop-proposal-producer.spec.ts
Expected: PASS (existing producer tests + the migrated schema test).
- Step 3: Delete the processor + its spec:
cd /Users/daniel/Project/hp/humanwork/.worktrees/specialist-value-output-p2plus
git rm api/src/haystack/jobs/correction-refinement.processor.ts api/test/correction-refinement.spec.ts
- Step 4: Edit
api/test/llm-callsite-metering.integration.spec.ts— remove theCorrectionRefinementProcessorimport (:73) and the singleit("haystack: correction categorisation meters purpose='classify' ...")block (:221-255). Leave the file's other callsite tests untouched. Verify the file still parses + its remaining tests pass:
cd api && npx jest test/llm-callsite-metering.integration.spec.ts
Expected: PASS (remaining callsite tests; the deleted block gone).
- Step 5: Compile check (the dangling import from the deleted processor must be gone):
cd api && npx tsc --noEmit 2>&1 | grep -iE "correction-refinement|CorrectionRefinement" || echo "no dangling correction-refinement references"
Expected: the "no dangling..." line.
- Step 6: Commit
git add api/test/loop-proposal-producer.spec.ts api/test/llm-callsite-metering.integration.spec.ts
git commit -m "refactor(haystack): delete correction-refinement processor + spec; migrate processed_at schema test (P4.2c)"
Task 3: Remove the feature flag
Files:
-
Modify:
api/src/feature-flags/feature-flags.constants.ts -
Modify:
api/src/feature-flags/feature-flag.service.spec.ts -
Step 1: Read
feature-flags.constants.ts:44-51and:105-108to see the exact comment blocks around the flag. -
Step 2: Remove the flag from
FLAG_KEYS(:50) and its OQ-205/ADR-037 comment block (:45-49), and fromDEFAULT_FLAG_VALUES(:107) and its comment (:106). Leave the surrounding flags intact. -
Step 3: Remove the golden assertion line —
feature-flag.service.spec.ts:53(thecorrection_auto_ingest_enabledentry inside theexpect(FLAG_KEYS).toEqual([...])array). The siblingexpect(rows.map(...).sort()).toEqual([...FLAG_KEYS].sort())at :58-59 derives from FLAG_KEYS and needs no manual edit. -
Step 4: Run the flag spec (the golden assertion must now match the trimmed FLAG_KEYS):
cd api && npx jest test/feature-flag.service.spec.ts src/feature-flags/feature-flag.service.spec.ts 2>&1 | tail -8
(Use whichever path the spec lives at — grep -rl "FLAG_KEYS).toEqual" api/src api/test.)
Expected: PASS — FLAG_KEYS golden assertion green with the flag removed.
- Step 5: Commit
git add api/src/feature-flags/feature-flags.constants.ts api/src/feature-flags/feature-flag.service.spec.ts
git commit -m "refactor(feature-flags): remove correction_auto_ingest_enabled flag (P4.2c)"
Task 4: Accept ADR-037 + update stale references
Files:
-
Modify:
docs/decisions/037-runtime-readonly-and-human-approved-promotion.md -
Modify:
docs/decisions/OPEN_DECISIONS.md -
Modify:
docs/superpowers/plans/2026-07-07-specialist-value-output-master-plan.md(optional marker) -
Modify:
api/src/learning/auto-correction.listener.ts,api/src/expert-queue/expert-queue.service.ts,api/src/common/entities.ts,api/src/haystack/haystack-exception.filter.ts,api/src/main.ts(stale comments) -
Step 1: Flip ADR-037 (
docs/decisions/037-...md), mirroring P4.1's ADR-035 accept (git show 026695ad -- docs/decisions/035*.mdfor the exact style):- Line 3:
**Status**: Proposed→**Status**: Accepted (2026-07-09; P4.2 shipped — proposal flow live, legacy direct-ingest removed) - Decision 3 (:29): change "...are removed once the human-approved proposal flow (master plan P4.2) ships; P4.2's consumer takes over the unprocessed backlog." → "...are removed — P4.2a (
LoopProposalProducerService) + P4.2b (approval UI) shipped, and P4.2c executed this contract step. P4.2a's producer consumes the unprocessed backlog as human-approved proposals." - Verification (:43-46): the
correction-refinement.spec.tsgate-block reference is now stale (spec deleted). Replace with the current guarantees: P4.2a producer spec (backlog consumed flag-independently as proposals) +feature-flag.service.spec.ts(the flag is no longer in FLAG_KEYS).
- Line 3:
-
Step 2: Update
OPEN_DECISIONS.md(~:115, CD-30): "pending acceptance" → "accepted (2026-07-09)"; "removed when P4.2 ships" → "removed (P4.2c)". -
Step 3: Update stale flywheel comments — in
auto-correction.listener.ts:11-12,expert-queue.service.ts:160,entities.ts:1511: change "correction-refinement → KB flywheel" (or similar) to reference the P4.2a loop-proposal producer as the downstream consumer. Inhaystack-exception.filter.ts:38andmain.ts:388, replace theCorrectionRefinementProcessor/ "correction-refinement" example with a surviving one (e.g.GoldenAnswerSyncProcessor/ "golden-answer-sync"). These are comment-only; do not change logic. Do NOT modifyauto-correction.listener's logic — it is the correction producer and stays. -
Step 4: Optional — mark P4.2 done in
master-plan.md:167if that file tracks per-task status (mirror how P4.1 marked D4 closed). -
Step 5: Verify no stale code refs remain:
cd /Users/daniel/Project/hp/humanwork/.worktrees/specialist-value-output-p2plus
grep -rn "correction_auto_ingest_enabled\|CorrectionRefinementProcessor\|CORRECTION_REFINEMENT_QUEUE" api/src api/test | grep -v node_modules || echo "no code references remain"
Expected: "no code references remain" (docs may still mention the flag historically — that's fine in ADR/OPEN_DECISIONS as accepted-history).
- Step 6: Commit
git add docs/ api/src/learning/auto-correction.listener.ts api/src/expert-queue/expert-queue.service.ts api/src/common/entities.ts api/src/haystack/haystack-exception.filter.ts api/src/main.ts
git commit -m "docs: accept ADR-037; retire correction-refinement flywheel references (P4.2c)"
Self-review checklist (run after implementation)
- Contract fidelity: flag gone from FLAG_KEYS + DEFAULT_FLAG_VALUES; processor + queue + tick + metrics enrolment gone; ADR-037 Accepted; P4.2a untouched.
- Zero frontend change:
git diff <base>..HEAD -- frontend/EMPTY. - P4.2a intact:
git diff <base>..HEAD -- api/src/config-assets/loop-proposal-producer.service.ts api/src/config-assets/loop-proposal.distil.tsEMPTY (the new pipeline is not touched). - No dangling refs: the Task-4 grep prints "no code references remain".
- Chesterton fences honoured:
LlmModule/FeatureFlagsModule/Correctionin haystack.module removed ONLY if grep proved no surviving consumer;auto-correction.listenerlogic untouched; schema test migrated not dropped. - processed_at semantics: only P4.2a writes it now (verified sole backlog reader).
Verification (verify skill, after all tasks)
cd api && npx tsc --noEmit→ clean, nocorrection-refinementrefs.cd api && npx jest feature-flag loop-proposal llm-callsite-metering haystack config-assets 2>&1 | tail→ all green (flag golden assertion, migrated schema test, metering file minus deleted block, haystack module wiring regression, P4.2a producer regression). Do NOT run fullnpm test(seed-eval spec self-rewrites migrations under full runs).cd api && npm run build→ EXIT 0 (catches any OnModuleInit/import residue in haystack.module).cd api && npm run lint→ clean.- Grep sweep (Task 4 Step 5) → "no code references remain".