Skip to content

feat: filter per-unit consumes by Unit kind with consumes[].kinds - #1132

Draft
vnlebaoduy wants to merge 1 commit into
awslabs:mainfrom
vnlebaoduy:feat/consumes-kinds
Draft

vnlebaoduy wants to merge 1 commit into
awslabs:mainfrom
vnlebaoduy:feat/consumes-kinds

Conversation

@vnlebaoduy

Copy link
Copy Markdown
Contributor

Summary

The engine piece from the review of #1003 (item 2): a per-unit stage can mark one consume as applying to some Unit kinds only. produces_kinds already prunes what a Unit must write; a consume had no kind axis, so a once-per-workflow artifact such as a UI mockup reached every Unit's directive and upstream-coverage asked a backend Unit to cite it — a false SENSOR_FAILED on correct output.

Refs #999, #1003. No stage consumes: changes here; #1003 can rebase onto this and tag wireframes / mockups / interaction-spec with kinds: [ui].

Changes

  • core/tools/aidlc-stage-schema.ts — consumes[].kinds?: string[]: non-empty, every entry in UNIT_KINDS, and refused on a stage that is not for_each: unit-of-work (the list would be silently inert).
  • core/tools/aidlc-lib.ts — consumeAppliesToKind() beside filterProducesByKind() (same semantics: no list or untagged Unit keeps the consume); the frontmatter parser accepts an inline [..] list on a consume subkey; the emitter writes it back.
  • core/tools/aidlc-orchestrate.ts — resolveConsumes drops a consume the Unit's kind is not listed for.
  • core/tools/aidlc-sensor.ts — upstream-coverage threading reads the Unit from the fired file's <record>/construction/<unit>/<slug>/ path and its kind from the unit DAG, then applies the same filter before presentConsumes. Stage-level per-unit layouts and untagged Units resolve to null and keep every consume.
  • core/tools/aidlc-graph.ts, core/tools/aidlc-plugin.ts — Consume.kinds carried through compile; the contribution-seam consumes regex tolerates the subkey.
  • core/aidlc-common/protocols/stage-definition.md, docs/reference/15-stage-definition.md — field row and a ### consumes[].kinds section next to produces_kinds.
  • tests/unit/t338-consumes-kinds.test.ts — parse/emit round-trip, validator accept/reject, consumeAppliesToKind, and the two spawned surfaces against a copy of the shipped graph (AIDLC_STAGE_GRAPH) with wireframes gated to [ui] on functional-design: a ui Unit's directive carries it, a service Unit's omits it, an untagged Unit keeps it; a service Unit's fire upstream-coverage passes on a deliverable that never cites wireframes while a ui Unit's fails naming it — the backend-Unit fixture from the fix: Frontend mockups design artefacts are not flowing to the downstream stages (#999) #1003 review.
  • tests/.coverage-registry.json, tests/.coverage-ratchet.json — regenerated (gen-coverage-registry.ts). Nine of the registry lines are pre-existing drift on main (function:copilotStopEvidence), not from this change.

No version bump, badge, or CHANGELOG entry, per the Release Metadata Policy.

User experience

Before: every per-unit stage input applies to every Unit; a stage author wanting a UI-only input had no way to say so, and declaring it made backend Units fail the advisory coverage sensor.

After: kinds: [ui] on the consume. Nothing changes for existing stages — none declare it.

Checklist

  • I have reviewed the contributing guidelines
  • I have performed a self-review of this change
  • Changes have been tested
  • Changes are documented
  • If this change adds an input to any fingerprint, epoch, or receipt identity, the description names the human-visible change it detects

Test plan

Head a5bb4264 against main 355903d6 (2.8.2):

bun run check                                                 # deterministic across 7 harnesses, typecheck, lint
bun test tests/unit/t338-consumes-kinds.test.ts               # 12 pass
bun tests/run-tests.ts --unit --integration --no-llm --filter "t207-|t208-|t116-|t65-|t86-|t233-|t92-|t188-"   # all PASS

The wiring is load-bearing: with aidlc-orchestrate.ts and aidlc-sensor.ts reverted to main, t338 fails exactly the two cases the review named — the service Unit's directive still carries wireframes, and its fire upstream-coverage reports it unreferenced.

Compile end to end: a copied shell whose functional-design declares kinds: [ui] compiles and stage-graph.json carries {"artifact":"wireframes","required":false,"kinds":["ui"]}; the same list on domain-design (not per-unit) fails compile with consumes[0].kinds requires for_each: unit-of-work.

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.

@vnlebaoduy

Copy link
Copy Markdown
Contributor Author

Rebased onto main @ 572c8265 (was c0eb2921). The only conflicts were the two generated coverage files; I took main's copies and regenerated them (bun tests/gen-coverage-registry.ts, --check reports "fresh, guards green, ratchet held"). One follow-on edit: t338-consumes-kinds.test.ts spawns the tool, so it is now listed in gen-coverage-registry.test.ts's EXPECTED_NONE_TO_CLI set, which the drift test requires by hand.

Verified on the rebased head ad402188: bun scripts/package.ts --check deterministic across 7 harnesses; lint and typecheck clean; the 64 unit files that mention consumes, the stage schema, or the graph all pass through tests/run-tests.ts --unit --filter.

@vnlebaoduy

Copy link
Copy Markdown
Contributor Author

Rebased onto main @ 891669aa (post-2.9.0). Only the two generated coverage files conflicted; regenerated with bun tests/gen-coverage-registry.ts (--check: fresh, guards green, ratchet held). t338-consumes-kinds and gen-coverage-registry pins 49 / 49; lint, typecheck, and packaging determinism green on 556ab645.

@vnlebaoduy
vnlebaoduy force-pushed the feat/consumes-kinds branch 2 times, most recently from 75a5355 to 89d0275 Compare September 20, 2026 14:49
@vnlebaoduy

Copy link
Copy Markdown
Contributor Author

Moving this to draft — it is not reviewable in its current shape, and I would rather say so than leave it in the queue.

Two blockers, neither of which I can resolve from outside:

1. It has no in-tree consumer. consumes[].kinds is a new public frontmatter key, and no stage in the tree sets it. I wrote it because @apackeer's review of #1003 named this exact shape ("a consumes[].kinds field mirroring produces_kinds ... If you would rather land the engine piece first as its own PR and rebase this one on it, that works too"), and #1003 would be the first consumer. #1003 has had no activity since 2026-09-03, so the key would merge alone, unused.

2. #1005 silently disables half of it. unitKindForOutput (core/tools/aidlc-sensor.ts) derives the unit from <record>/construction/<unit>/<stage>/ by requiring rest[1] === stage.slug. #1005 moves per-unit artifacts to construction/units/<unit>/<stage>/, which makes rest[1] the unit name, so the check fails and the function returns null — and null means "keep every consume". After #1005 the directive would still be filtered but the sensor would thread every consume again, reintroducing the false SENSOR_FAILED on backend units that this PR exists to remove. I verified the two path shapes against the parser rather than assuming it.

@apackeer, two questions, whichever is cheaper to answer:

  • Should a kind filter land as consumes[].kinds now, or wait for the reserved when: predicate? docs/reference/15-stage-definition.md:584 reserves when for v0.10.0 to "supersede consumes[].conditional_on and generalise today's scope-aware consumes[].required with richer predicates", and a kind filter is arguably one of those predicates. If when: is the intended home, this PR should be closed rather than parked.
  • If kinds is still wanted, is it better rebased onto fix: separate per-unit artifact folders from stage folders under construction/ (#862) #1005 so the sensor half is written against the final layout? I can also make the unit-kind lookup path-independent so the layout change cannot disable it either way.

Happy to close this if the answer is "wait for when:" — no need to keep it open on my account.

@vnlebaoduy
vnlebaoduy marked this pull request as draft September 21, 2026 15:06
A per-unit stage can now mark one consume as applying to some Unit kinds
only, the consumer-side twin of produces_kinds. The directive builder
(resolveConsumes) drops the consume for a Unit of another kind and the
upstream-coverage dispatcher stops threading it, so a backend Unit is no
longer asked to cite a UI mockup it never read. No list, or an untagged
Unit, keeps the consume.

Schema: `kinds` is a non-empty inline list of UNIT_KINDS and is refused
on a stage that is not `for_each: unit-of-work`, where it would be inert.
The frontmatter parser accepts an inline list on a consume subkey, the
emitter writes it back, the plugin contribution regex tolerates it, and
graph compile carries it into stage-graph.json.

No core stage declares `kinds` yet; awslabs#1003 can tag wireframes / mockups /
interaction-spec once this lands.

Refs awslabs#999, awslabs#1003

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant