Skip to content

feat(workflow-orchestration-plugin): ship verify-before-filing workflow harness template #2167

Description

@laurigates

Tier A #1 — the strongest case in the set. Zero refutations across all three adversarial lenses.

Source: docs/plans/dynamic-workflow-migration.md §1 (merged in #2162).

Why this one wins

Agent count is identical before and after. REFERENCE.md already ships the JS skeleton and the skill already runs this shape — so there is no token-cost objection to answer. What the harness buys is that the gate

['still-present','partially-fixed'].includes(verdict) && duplicateFound === 'no'

stops being a boolean the model re-derives from prose on every run.

Shape: composite — fan-out (verify ∥ search per candidate) → generate-and-filter (draft → cold-read → batch dedup).

Two caveats the adversarial pass added

  1. Waves of ≤5. The skill paces its writes at 70 s but left reads unbounded. A 24-candidate manifest would fire 48 concurrent agents at an instance the skill itself calls aggressively rate-limiting.
  2. DRAFT_SCHEMA carries the body, not a path. Batch-dedup cannot merge on a file path a workflow script has no filesystem to read.

Sketch

export const meta = { name: 'verify-before-filing',
  phases: [{title:'Verify'},{title:'Search'},{title:'Draft'},{title:'ColdRead'},{title:'Revise'},{title:'BatchDedup'}] }

const CANDIDATES = (typeof args === 'string') ? JSON.parse(args) : args.candidates
const WAVE = 5                                     // pace reads like Phase 3 paces writes

const triaged = []
for (let i = 0; i < CANDIDATES.length; i += WAVE) {
  phase(`Verify + dedup — candidates ${i + 1}..${Math.min(i + WAVE, CANDIDATES.length)}`)
  const wave = await pipeline(CANDIDATES.slice(i, i + WAVE),
    async (item) => {
      const [verify, search] = await parallel([     // BARRIER (intra-candidate): the gate needs BOTH
        () => agent(VERIFY_PROMPT(item), {label:`verify:${item.id}`, phase:'Verify', schema:VERIFY_SCHEMA}),
        () => agent(SEARCH_PROMPT(item), {label:`search:${item.id}`, phase:'Search', schema:SEARCH_SCHEMA}),
      ])
      if (!verify || !search) return {...item, disposition:'agent-error'}
      const passes = ['still-present','partially-fixed'].includes(verify.verdict)
                  && search.duplicateFound === 'no'  // gate precedence: a duplicate kills regardless of verdict
      return {...item, verify, search, disposition: passes ? 'draft'
        : (search.duplicateFound !== 'no' ? `duplicate: ${search.wave1Overlap}` : verify.verdict)}
    },
    async (c) => {                                   // draft only survivors; schema carries body, not just path
      if (c.disposition !== 'draft') return c
      const draft = await agent(DRAFT_PROMPT(c), {label:`draft:${c.id}`, phase:'Draft', schema:DRAFT_SCHEMA})
      return draft ? {...c, draft} : {...c, disposition:'draft-failed'}
    },
    async (c) => {                                   // adversarial gate — NEVER the drafter
      if (!c.draft) return c
      let cold = await agent(COLDREAD_PROMPT(c.draft.body),
        {label:`coldread:${c.id}`, phase:'ColdRead', model:'haiku', schema:COLD_SCHEMA})
      if (cold?.verdict === 'needs-revision') {
        const rev = await agent(REVISE_PROMPT(c.draft, cold.critique), {label:`revise:${c.id}`, phase:'Revise', schema:DRAFT_SCHEMA})
        if (rev) c = {...c, draft: rev}
        cold = await agent(COLDREAD_PROMPT(c.draft.body),
          {label:`recoldread:${c.id}`, phase:'ColdRead', model:'haiku', schema:COLD_SCHEMA})
      }                                              // exactly ONE revise round — a fixed gate, not a loop
      return {...c, cold, disposition:'file'}
    })
  triaged.push(...wave)
}

phase('Batch dedup — BARRIER: survivors compared to EACH OTHER, not just to the tracker')
const filing = triaged.filter(c => c.disposition === 'file')
const batch = await agent(BATCH_DEDUP_PROMPT(filing), {label:'batch-dedup', phase:'BatchDedup', schema:BATCH_SCHEMA})
return { file: batch.keep, merged: batch.merged,   // Phase 3 (scripts/file-wave.sh) consumes this
         dispositions: triaged.map(c => ({id:c.id, slug:c.slug, disposition:c.disposition})) }

SKILL.md delta

Under ## The Pipeline, add the standard template framing, then:

Phases 1–2 ship as verify-before-filing.workflow.js. Non-negotiable across any adaptation: the closed verdict vocabulary; the gate expression (a duplicate kills the filing regardless of verdict); the cold-read agent being a different agent from the drafter; the batch-dedup barrier; and the ≤5 read wave. A 24-candidate run is roughly 100–140 agents — if your run has one candidate, skip the workflow and do Phase 1 inline.

Acceptance

  • workflows/verify-before-filing.workflow.js beside SKILL.md
  • ## Workflow harness (template) framing present, incl. not a script to run verbatim
  • Wave cap and body-carrying DRAFT_SCHEMA both present
  • Lands in the same commit as its SKILL.md framing (docs-currency.md)

Blocked by #2166 (extract scripts/file-wave.sh — the harness returns the shape that script consumes) and #2163 (layout convention).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions