-
-
Known Unknowns: decide first, then build.
-
One click creates a decision ticket; the blocked story is filled in.
-
Spec readiness on a Jira work item: Not ready, with each blocking question linked to its place in the spec.
-
Open questions found deterministically on a Confluence spec (table, TBD, question).
-
The same four tools for Rovo and for coding agents through the Atlassian MCP server.
-
Decision log macro: every decision sealed in a per-page hash chain, verified on view.
-
Decisions are written into the spec next to the question.
-
The Rovo agent answers "Is it ready to build?" from the same data.
Inspiration
Every spec has them: a TBD in the requirements table, an "Open questions" list nobody closed, an empty Outcome cell, an inline comment asking "should partial refunds be instant too?". Teams start building anyway, and now AI coding agents do too, so the undecided parts get decided by accident, in code, by whoever touches them first.
What it does
Known Unknowns is a Forge app for Confluence and Jira that makes undecided questions visible and keeps work from starting on them until a person decides:
- Finds the open questions in specs, deterministically: TBD/TBC/"to be confirmed" markers, Open questions sections, question tables with empty outcomes, undecided decision pages and question-like inline comments. No LLM in the detection path: same page in, same questions out.
- Shows whether a Jira work item is ready to build: a Spec readiness panel and lozenge on the work
item (Ready / Ready with notes / Not ready), stored as a JQL-searchable issue property
(
kuStatus = "not-ready"). - Turns a question into a decision: one click creates a "Decide: …" ticket that blocks the story. Recording the decision (in the Confluence banner, the Jira panel, through the Rovo agent, or by closing the ticket with a "Decision:" comment) writes it back into the spec next to the question, closes the ticket and re-checks every work item that uses the spec.
- Seals every decision in a per-page SHA-256 hash chain; the Decision log macro verifies the chain every time the page is viewed.
- Gives AI agents the same brakes: a Rovo agent and a rovo:mcp server with the same four tools (check readiness, list questions, create decision ticket, record decision).
How we built it
Forge modules: jira:issueContext (with a dynamic lozenge), jira:entityProperty (JQL aliases kuStatus, kuBlocking, kuOpen), confluence:pageBanner, confluence:contentBylineItem, confluence:globalPage, macro, triggers (page created/updated, issue updated), a daily scheduledTrigger, rovo:agent, rovo:mcp and four actions. UI Kit frontends; Confluence REST v2 and Jira REST v3; everything a person triggers runs asUser(); Forge KVS with fail-if-exists keys for append-only receipts. The detection and write-back logic is pure code, separate from the platform calls, and covered by 25 tests against an in-memory Jira/Confluence; every flow was then checked on a real Atlassian site.
Challenges we ran into
- Writing back without wrecking the page. The decision goes where the question is: the Outcome cell, a nested "Decision:" bullet, a ticked task, a native decision after a TBD, a decision page's outcome, or a reply that resolves the inline comment, with a retry on version conflicts.
- Jira's Blocks link reads the opposite way from its field names ({inwardIssue: A, outwardIssue: B} means "A blocks B"). Caught on the live site, fixed and pinned by a test.
- Fast typing lost characters. UI Kit text fields that round-trip
valuethrough the bridge dropped keystrokes when someone typed quickly; our first live decision was sealed as "." (the log is append-only, so it stays). Inputs are now uncontrolled, and a decision must contain a letter or digit. - Narrow panels. In Jira's list-view side panel the lozenge was cut to "Not re…"; the counts now get their own line. Banner reads were parallelised to show it sooner.
- Staying inside the free tier: fingerprinted scans, small per-page summaries, a daily safety-net rescan.
Accomplishments that we're proud of
End to end on a live site: a sample PRD with 6 open questions → its story "Not ready · 6" → a decision ticket that blocks the story → a decision recorded from Confluence, written next to the question and sealed as receipt #1 → the ticket closed in Jira with the comment "Decision: Adyen for all EU markets in v1.", written into the spec's Outcome cell automatically in under a minute (receipt #2) → the story re-checked: 4 blocking questions left → the Decision log macro on the page shows "Chain verified · 2" (4 open · 2 decided) → asked "Is PLAT-33514 ready to build?", the Rovo agent answers "NOT READY — 4 blocking questions.", lists each one with a link to the spec and offers to create decision tickets for them.
What we learned
Deterministic detection is enough for most "known unknowns"; the hard part is the write-back and the round trip between the two products.
What's next
A Marketplace-ready single-product variant, Teamwork Graph signals (who usually decides what), reminders for decisions that stay open too long.
AI disclosure
Built with Claude (Anthropic) as the coding assistant. A human owner made the product decisions, created the accounts and approved every external action. The demo video is assembled from real screenshots of the demo site (zoom, highlight boxes, captions); its narration is offline text-to-speech (Kokoro-82M).
Built With
- confluence
- forge
- javascript
- jira
- mcp
- node.js
- rovo
- ui-kit