the-custodian/research/WorkOrchestrationArchitectureDraft.md
codex 91dd521b8f
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 4s
WorkOrchestrationArchitectureDraft v0.2: founder review resolves all 7 open questions
- umbrella term 'work record'; intake kind 'intake item'
- any repo file is a work-record source; generated per-repo index
  (transclusion-style, markitect scope) replaces separate authored file
- engagements are their own kind
- ordering simple (lane+priority+age); deps optional; WSJF optional,
  situational overload-triage only
- issue-core repositioned as third-party tracker connector (UUID<->
  external id, boundary sync), out of the internal loop
- fresh intake entity in state-hub; suggestions to read-only legacy
- budgets hybrid: repo budgets.yaml + frontmatter override + global
  best-guess default; enforcement programmatic, never agentic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:48:15 +02:00

18 KiB
Raw Blame History

Work Orchestration Architecture — Draft for Discussion

Draft v0.2 — 2026-07-20 (v0.1 2026-07-19). Synthesis of 2026-07-19-work-orchestration-infrastructure-survey.md (internal) and 2026-07-19-work-orchestration-best-practices.md (external). Trigger: reconciling the binky-control automation queues with the suggestion/task/workplan backbone (AWQ-010 case). Status: proposal, founder-reviewed — the seven open questions were resolved with the founder on 2026-07-20 (§8); terminology extensions are marked ⟨new⟩ and land as canon standard drafts next.

0. Thesis

The queues were not a mistake — they were a market signal. Intake through a one-YAML-block append beat the hub suggestion flow because its friction was an order of magnitude lower, and it carried routing information (lanes) the backbone lacks. The error was ontological, not practical: the queue items became a second, unindexed species of work item.

The reconciliation is therefore not "move queues into the hub" but: make the backbone cheap enough at intake that the queue pattern becomes a schema-valid, hub-indexed instance of it — and make every future species impossible to create outside the reconciliation loop without tripping an alarm.

One sentence: everything with an ID is a work record, every work record is a repo file, every repo file is indexed, and every list anyone works from is a derived view.

1. The unified ontology

1.1 Work record ⟨new umbrella term⟩

A work record is any identified, lifecycle-bearing coordination artefact. All existing species become kinds of work record:

work record
├── intake item ⟨new⟩      — spark: idea, mail finding, observation, request
│     (absorbs: AWQ items, hub suggestions, "actionable" mail-triage rows)
├── task                   — smallest executable unit (canon lifecycle)
│     (absorbs: workplan tasks, adhoc tasks, issue-core issues-as-interface)
├── workplan               — structured, dependency-bearing body of work
├── decision               — approval package + resolution
│     (absorbs: DEC items; hub decision records become the index)
├── engagement ⟨new⟩       — scheduled external interaction
│     (absorbs: OH items — office-hour/command-day bookings)
└── register entry         — risk, technical debt, capability request, CCR,
                             interface change (existing species, unchanged
                             semantics, brought under the same conventions)

Every work record, regardless of kind, carries the same conventional spine (aspect 4 — one framework, many demands):

Field Convention
id canonical name per type registry (§1.3)
uuid UUIDv7, hub-assigned at first index, written back to the file (like state_hub_*_id today)
kind from the closed kind list above
lane green/blue/yellow/orange/red (§2)
status abstract canon lifecycle for its kind (§1.4)
owner repo-anchored owner (agent identity or human)
repo owning repo (ADR-001/ADR-005 anchor)
tags policy + derived tags (§3)
created/updated dates; UUIDv7 provides fine-grained internal time

Kind-specific fields (budget, evidence, deadline_pressure, attention_cost, …) extend the spine; they never replace it.

1.2 File-first, one reconciliation loop (aspect 3)

  • Every work record originates in a repo file — and any file in a repo is a potential source of work records. Workplans stay dedicated files; intake items, decisions, engagements are YAML blocks authored in context: thematic live documents, meeting notes, design docs, triage logs. The schema is the contract, not the location. (Motivating example: meeting notes with todos are kept where the work happens, yet the todos must surface in one global orientation view — see the index below.)
  • A fully generated per-repo work-record index captures every registered record with a reference to its source location — a transclusion-style view: the record lives once in its source file, the index (and other views) reference it. Registration binds each record to hub state wherever it was authored, which stabilizes concurrent human/agent edits and enables cross-checks instead of causing conflicts. Efficient transclusion and index management is markitect-family scope.
  • Forgejo CI validates schemas on push (yaml-block schema per kind — the GitOps "validate before apply" rule).
  • fix-consistency grows checks per kind (as C-06 does for workplans): registers new records, writes UUIDs back, syncs status from file to hub. The hub stays a read model; runtime operations data (logs, metrics, run histories, token events) is the sanctioned exception and lives only in hub/activity-core/monitoring stores.
  • Derived views (§4) are generated from the hub, never hand-maintained: the "Completed" tail of AutopilotWorkQueue.md, daily-brief queue summaries etc. become materialized view output (ADR-003 pattern), clearly marked [auto].

1.3 Type registry and layered identity (aspect 5)

A single canon file (canon/standards/work-record-types_v0.1.md + machine copy work-record-types.yaml) registers every id scheme:

- code: WP        # {PREFIX}-WP-NNNN          workplan
- code: T         # {WP-ID}-TNN               task
- code: IN        # {PREFIX}-IN-NNNN          intake item   ⟨new; AWQ-* grandfathered⟩
- code: DEC       # {PREFIX}-DEC-YYYY-NNN     decision
- code: ENG       # {PREFIX}-ENG-YYYY-NNN     engagement    ⟨OH-* grandfathered⟩
- code: RISK, TD, CAP, CCR, ADR …             register entries (existing)
  • UUIDv7 is primary for bookkeeping, relations, dependencies, history — stable across renames, migrations, and terminology transitions (the workstream→workplan migration cost is the proof this matters).
  • Canonical names are primary for humans and agents in UI/CLI/API output, prose, and commits; UUIDs inspectable on demand. This is already practiced for WP/T — the registry universalizes it.
  • Grandfathering rule (from the terminology standard): existing ids (AWQ-, OH-) are never renamed; the registry maps legacy prefixes to kinds and the legacy-meter discipline governs retirement of the schemes, not the records.

1.4 Abstract states + flow profiles (aspect 4)

  • The abstract lifecycles stay minimal and canon-fixed: task wait/todo/progress/done/cancel (InfoTechCanon); workplan proposed/ready/active/blocked/backlog/finished/archived; intake item open → vetted → routed → closed(promoted|declined|absorbed) ⟨new, replaces both suggestion stages and queue-item ad-hoc states⟩; decision prepared → resolved(approved|rejected|revised|deferred); engagement queued → prepared → done.
  • Different work kinds (development, ops, compliance, billing, sales…) get their demands met by flow profiles on the existing task-flow engine: named workstation definitions whose entry/exit assertions encode the kind-specific gates (e.g. a compliance task cannot exit progress without an evidence link; a sales engagement cannot enter prepared without prepared_material). Profiles are YAML in repos → same reconciliation loop. States stay abstract; rigor lives in assertions.

2. Lanes become fleet canon (aspects 1, 6, 7)

Promote binky-control's AutonomyPolicy lane model to canon/standards/autonomy-lanes_v0.1.md, unchanged in substance:

  • Green (safe autonomous) · Blue (reversible, logged) · Yellow (deferred approval) · Orange (time-window approval) · Red (human-only).
  • lane is a mandatory field on every work record and on every agent manifest; the harness already enforces lane-scoped tool profiles — this closes the loop from item to execution permission.
  • Human attention is modeled as a WIP-limited workstation: the founder attention budget (minutes/day, max decisions/day) is a lane capacity; Yellow routes to the decision queue view, Orange to the engagement view, both subject to that capacity. External research is unanimous that risk-tiered gating is the only scalable human-oversight pattern.
  • Time-boxing (needed_by, fallback_if_no_response) is hub-owned: the hub (not a human reading a file) raises the escalation when a window lapses — via the existing NATS → activity-core delegation.

3. Policy tags, not states (aspect 6)

Two tag classes, both in the tags field, never in status:

  • Derived tags — computed by hub/activity-core from data, read-only in files: stalled (work-item age over threshold — the flow-metrics form), stale (reviewed against older repo state), overdue-decision, budget-breached, orphaned (no goal/workplan link).
  • Policy tags — assigned by agents/humans under tagging policies: automatable, needs-human, unclear-implementation, red-adjacent (touches a Red boundary), compliance-relevant. Tagging policies are YAML rules (activity-core ActivityDefinitions may auto-apply them on events) — auditable and evolvable without schema migrations.

Tags drive the optimization loop: automatable feeds the autopilot view; needs-human + lane feeds attention routing; stalled+unclear feeds the kaizen coach. This is how "what should be automated next" becomes a query instead of a discovery.

4. Queues are views (the reconciliation itself)

The three binky queues are retained as products, redefined as views:

Today Becomes
AutopilotWorkQueue.md (hand-edited) Autopilot view: kind=intaketask, lane∈{green,blue}, status∈{open,todo}, tag=automatable, ordered by lane+priority+age (§4.1); "never empty" becomes a monitored invariant with an alert, not a plea
DecisionQueue.md open section Decision view: kind=decision, status=prepared, ordered by needed_by; resolved log is generated
OfficeHourQueue.md Engagement view: kind=engagement, batched per command day; runbook generation stays (officehour/)
— (new) Per-repo work-record index (§1.2): every registered record, any kind, any source file — the "all my todos across everything" orientation view

Mechanics: records are authored in their source files (cheap intake preserved — one YAML block, schema-checked, authored where the context is); fix-consistency indexes them; view documents and the index are regenerated [auto]. Pull protocol becomes a status transition (todo → progress with owner) instead of a prose note — which makes claims visible fleet-wide and collision-free.

4.1 Ordering (resolved 2026-07-20)

Default ordering is deliberately simple: lane + priority + work-item age — zero extra fields at intake. Dependency capture is optional per record (edges when they matter, never mandatory). WSJF is an optional, situational triage instrument for capacity-overload situations, applied to a view when selection pressure justifies estimation cost — never a required field. Rationale: small tasks implement faster than they can feasibly be planned and evaluated beforehand; mandatory estimation is waste at the bottom of the size distribution.

4.2 issue-core: third-party connector, not core loop (resolved 2026-07-20)

issue-core's role is a connector framework to external issue-tracking systems (Forgejo issues, Jira, GitHub, …): it maintains the mapping between internal work-record UUIDs and external issue ids and provides two-way, transclusion-like sync at the boundary when we collaborate through a third-party tracker. It is not part of the internal execution loop — claiming and execution run on work records directly (harness intake via NATS/activity-core per its stated target). Until an external integration is actually switched on, the connector layer adds zero load.

Promotion (the AWQ-010 lesson) becomes a first-class transition: intake.routed → workplan|task|decision|engagement with back-links (promoted_to/origin), performable by one CLI/MCP call that writes both files and re-syncs — never a manual transcription again.

5. Visibility, tenancy, scale (aspect 2)

  • state-hub remains the dev-coordination read model (localhost, file-first, fix-consistency).
  • core-hub is the production visibility layer: dashboards, flow metrics (WIP, cycle time, item age, throughput — all computable from UUIDv7 + status transitions), cross-domain rollups, operator console. The work record spine becomes part of the core-hub contract/IR so Gen-3 ingests ADR-001 artefacts natively (its workplan-coordination spec already commits to this).
  • Tenancy: repo → domain → tenant, derived from repo classification (never a parallel spine). agent-harness is already multi-tenant with per-tenant credential lanes (ops-warden/OpenBao); dashboards get tenant/domain scoping from the same derivation. Separation is by classification + credential lanes; centralization is by shared runtime (harness, activity-core, hubs) — efficient utilization with clean blast radii.
  • Budgets as guardrail envelopes (aspect 1, resolved 2026-07-20): three-level resolution, strictly programmatic —
    1. workplan frontmatter override (narrowest, lives with the work),
    2. per-repo budgets.yaml lane/default envelopes (the auditable guardrails),
    3. global best-guess default when neither exists — nothing ever runs unbounded. The harness resolves override → repo policy → global default at run start; record_token_event meters; budget-breached tag + escalation on breach. Budget resolution and enforcement is automation infrastructure code, never agentic effort — AI tokens and personal review only refine the estimates over time. Complex-project risk control = dependency edges (existing) + lane gates + budget envelopes + flow metrics, all on one spine.

6. Opinionated by construction (aspect 7)

  • The registry is closed. An id pattern not in the type registry fails CI and fix-consistency (the "sidetrack detector" — a warning that names the reintegration cost explicitly). New kinds are added by canon PR, which is deliberately cheap (one YAML entry + one schema) so the legitimate path is easier than the workaround — the core lesson of the queues.
  • AGENTS.md generation (state-hub templates) teaches the conventions in every repo; the guidance and the enforcement come from the same source.
  • Intake friction budget: creating an intake item must never cost more than the AWQ pattern did (append one block). If a future workaround appears anyway, the postmortem question is fixed: which friction did we fail to remove?

7. Migration path (staged, history-preserving)

  1. Canonize (docs only): work-record umbrella + type registry + autonomy-lanes standard + intake lifecycle → canon standards v0.1; extend workplan-terminology-fleet rather than fork it.
  2. Schema + CI: YAML schemas per kind; Forgejo CI check; binky-control is the pilot (its queues become schema-valid without renaming ids).
  3. Index: fix-consistency checks for intake/decision/engagement kinds (C-2x series); hub tables reuse existing entities where they fit (suggestions table ⇢ intake items — suggestions gain file artefacts, resolving their own ADR-001 tension; decisions already dual-exist, the check just automates the mirroring both ways).
  4. Views: generated [auto] sections replace hand-maintained queue tails; hub owns needed_by clocks via NATS → activity-core.
  5. Tags + metrics: derived-tag computation and the four flow metrics in state-hub first, surfaced in core-hub dashboards as the contract lands.
  6. Retire legacy singularities per legacy-meter discipline (e.g. the hub-first suggestion create path) only after zero measured use.

Each stage is independently valuable; stopping after any stage leaves the system consistent (no big-bang).

8. Resolved questions (founder review, 2026-07-20)

  1. Naming: umbrella term is work record; the intake kind is intake item ("suggestion" remains only as the legacy bridge name). Mental model: a task is a specific kind of work record — the smallest executable unit; the work-record supertype guarantees the shared spine across all kinds.
  2. Where records live: any repo file is a potential source of work records (meeting notes, thematic docs, design docs); a fully generated per-repo index references all registered records in their source locations (transclusion-style; markitect-family tooling). See §1.2. Registration binds records to hub state wherever authored — stabilization and cross-checking for concurrent human/agent work, not a conflict source.
  3. Engagements are their own kind — a command day batches many engagements; an engagement may spawn or resolve several decisions but is not itself one.
  4. Ordering: simple default (lane + priority + age); dependency capture optional; WSJF optional and situational — a triage instrument for limited capacity under overload, not a universal method. Small tasks implement faster than they can feasibly be planned and evaluated. §4.1.
  5. issue-core is a connector framework to third-party issue trackers (internal UUID ↔ external issue-id mapping, two-way transclusion-like sync at the boundary); not part of the internal loop; must add no load until an integration is actually used. §4.2.
  6. Hub entity: fresh intake entity with the clean UUIDv7 spine; the suggestions table becomes read-only legacy with a one-time close-out of its few records.
  7. Budgets: hybrid — per-repo budgets.yaml guardrails + workplan-frontmatter overrides + a global best-guess default as final fallback so nothing runs unbounded; resolution/enforcement is programmatic infrastructure, never agentic effort. §5.

Prepared from live fleet data (AWQ-010 reintegration, DEC-2026-003/004 dual bookkeeping, BINKY-WP-0002 slug drift) and the two research artefacts alongside this file. All open questions resolved with the founder 2026-07-20. Next step: canon standard drafts (work-record types, autonomy lanes, intake lifecycle) + a CUST workplan for migration stages 12.