Skip to content

Server backlog: unfinished work from #189, #232, #322, #323, #324 #337

Description

@m2ux

Consolidation of the server-side work left unfinished by five issues whose PRs merged partially. Those issues are now closed; this one and its corpus counterpart carry everything that did not ship.

Superseded issues: #189, #232, #322, #323, #324. Each item below names the issue and item id it came from, so the original evidence stays reachable.

Companion issue: #338 — the corpus half. Every "companion issue" reference below means that one.

Delivery routing: #343 — which of these items ride together in a pull request, in what order, and which stay out.

Every item opens with an In plain terms paragraph — what the problem actually is, readable without knowing the codebase shorthand. The technical detail follows it.

Scope

Server repo only — src/, scripts/, tests/, docs/. Corpus items are in #338.

Two of these span both repos (S1 and S7); the corpus half is called out and cross-referenced rather than duplicated.


S1 — Key the delivery ledger by receiving context, not session agentId · L

In plain terms. The server keeps a record of what it has already sent, so it does not send the same content twice. That record is filed under the session's id. But one session hands work out to many separate worker agents, each a fresh context that has received nothing and remembers nothing of the others. Because they all share one filing drawer, the server believes a worker already holds content that in fact went to a different worker which no longer exists — so the new worker gets nothing and cannot do its job. The corpus works around this by forbidding the economical delivery mode outright. Fix the filing and that ban can be relaxed, which is where the ~96k tokens per run comes from.

From #323 T3 / #322 R3. The single largest measured win still on the table, and the only item here with a number attached to it.

deliveredHash reads state.deliveredContent?.[state.agentId]?.[key]. agentId is session-level, so dispatched per-activity workers share one ledger namespace regardless of the worker agent_id that dispatch-activity composes into their prompt. The ledger therefore records deliveries made to contexts that no longer exist — which is why workers-need-full-delivery must forbid persistent, and why reference delivery is unreachable by rule rather than by capability in the topology every client workflow actually runs.

Needs a design decision before implementation, recorded as an ADR with the rejected option:

  • (a) Namespace the ledger by the composed worker identity dispatch-activity already produces. No new tool parameter; requires that identity to reach the server.

  • (b) Add an explicit delivery scope parameter to get_activity. Explicit and testable; widens the tool surface and the orchestrator contract.

  • ADR recorded with the rejected option and why

  • Ledger namespaced per receiving context; cross-worker collapse impossible

  • Test: two workers in one session do not collapse against each other's deliveries

Acceptance: a dispatched walk reaches the persistent-mode figure on the activity/technique/workflow paths — 1,057,824 → 673,926 chars, −36%, ≈96k tokens per 12-activity walk (measured in #322).

Corpus half: relaxing workers-need-full-delivery from "no ledger" to "fresh ledger per worker", with dispatch-activity and create-session updated coherently. Tracked in the companion issue; S1 is the prerequisite and must land first.

S2 — Reconcile artifacts_produced against the planning folder · M

In plain terms. Workers declare which files they created. Nothing checks that declaration against what is actually on disk, and the save step commits the whole planning folder regardless. So files nobody declared — and nobody reviewed — are committed silently. This is not hypothetical: two plan documents and twenty assumption rows once appeared from outside the normal activity loop and went straight into a commit no one had read.

From #324 B3. Worker manifests are self-reported and the orchestrator's persist hook stages the planning directory wholesale, so undeclared artifacts get committed unseen. Demonstrated in the #141 run: 06-work-package-plan.md, 06-test-plan.md and 20 PL-* assumption rows appeared from outside the activity loop and entered a commit nobody had read.

  • Diff the planning folder against the accumulated manifest at next_activity
  • Surface undeclared files to the orchestrator rather than silently staging them

S3 — Aggregate per-activity usage and derive a cost estimate · S

In plain terms. The server now records how many tokens each activity burned, but only as a list of separate rows — it never adds them up. The blocker is not arithmetic: when a worker is resumed part-way, some harnesses re-report the running total and others report only the new work, so adding the rows could double-count. Decide which convention holds, write it down, then produce a per-run total and a money figure from it.

From #232 US-2. PR #329 landed the per-activity record (the usage parameter on next_activity and inspect_session view: usage), satisfying US-1. What it deliberately did not do is sum: the view returns one row per activity exit because harnesses disagree about whether a resumed worker re-reports cumulative figures or just the delta, so no convention was picked.

  • Pick and document the resumed-worker convention (cumulative vs delta), so summing is well-defined
  • Per-workflow aggregate across activities
  • Cost estimate derived from the aggregate

US-3 (writing the record into the planning artifact on completion) is corpus work and sits in the companion issue; it consumes this.

S4 — Block-level dedup ledger for repeated contract/rules blocks · M

In plain terms. Technique documents repeat the same contract and rules boilerplate over and over. The server already avoids resending a whole technique it has sent before, but it cannot see repetition inside those documents — and that repeated material is roughly a quarter of what technique delivery costs. Catching it means tracking blocks rather than files.

From #189 C2 (cluster 3, never shipped). Repeated contract/rules content inside technique payloads is ~25% of technique delivery and the current per-technique dedup cannot see it. Measure against the fresh-mode gate S6 provides, not against a persistent-only comparison.

S5 — Close the fidelity-observability gaps · M

In plain terms. The observability work shipped its main body and left three specified pieces unbuilt: checking that resources actually resolve, emitting events at step granularity rather than only at activity boundaries, and being able to filter what you are looking at down to a single agent.

From #189 C11 (solo, never shipped). Resource validation, step events, per-agent filtering — the follow-through left by B8.

S6 — Guard and benchmark reach · S

In plain terms. Three separate ways the project's own measurements were lying to it. Two are now fixed; one decision remains.

Three separate defects that all make measurement lie, from #324 C2/C3/C4:

  • C2 — DONE. In plain terms: the tests always read the corpus from one hardcoded folder, so if you pointed the server at a corpus somewhere else, the tests carried on checking the old one and passed while proving nothing. Verified fixed — tests/fragments-guard.test.ts, tests/schema-validation.test.ts, tests/borrowed-technique-resolution.test.ts, tests/technique-addressing.test.ts and tests/technique-loader.test.ts all now import tests/corpus-root.ts.
  • C3 — DONE. In plain terms: the token benchmark could only ever measure one workflow, because that workflow's name was written into the code. It reported "no change" for changes it structurally could not see. Verified fixed — scripts/run-token-benchmark.ts:381 takes a --workflow argument.
  • C4In plain terms: when a workflow keeps a README inside its resources folder, that file's generated id collides with the workflow's own top-level README, and one silently hides the other. Decide whether the fix is id derivation (server) or a corpus naming rule, and if the latter, hand it to the companion issue.

S7 — when vs condition merge · M, medium risk

In plain terms. The schema offers two different ways to write "only run this step if…", and they have drifted apart. The structured form is marked legacy on ordinary steps, which is exactly why authors keep reaching for the string form instead. Pick which one survives, change the schema, and the corpus then migrates its seventeen uses. This is the only item here that can break existing content, so it stays on its own branch.

From #189 C8. The only migration-risk item in the epic; keep it isolated. Decide the merge, change the schema, then the 17 corpus uses migrate in the companion issue. Note the constraint recorded during #311: the schema currently marks structured condition: LEGACY on non-checkpoint steps, which is why when: is still being written.

S8 — get_workflow slimming on resume · S, conditional

In plain terms. When a session resumes, the server re-sends the entire workflow definition. Trimming that would save tokens — but only if resumes happen often enough to be worth the work. Resume traffic is measurable now, so look at the number before building anything.

From #189 C12. Gated on resume traffic showing it matters. With #311 having gated the saved-session search on stated resume intent, resume traffic is now measurable — check before building.

S9 — B12 retire sweep at the next schema major · S

In plain terms. A batch of deprecated constructs can only be deleted when a breaking schema version is cut. There is nothing to do until that happens; this is recorded so it is not forgotten when it does.

From #189 C13. Nothing to do until a major is cut; carried so it is not lost. Corpus follow-on in the companion issue.


Carried items that are already resolved — recorded, not to be redone

Sequencing

S1 first — it unblocks the corpus half and carries the only quantified win. S6's remaining sub-item (C4) is small. S2, S3, S5 are independent. S7 stays isolated. S8 is conditional, S9 waits for a major.

Note that #341 and #342 both touch scripts/check-binding-fidelity.ts, as does S6 C4's server-side option. If C4 is fixed server-side, take all three in one pass over that guard rather than three.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions