feat(agent-team): Confluence-writer node (draft → approve gate → dry-run write) [flag-gated] #66

Closed
amoussa1229 wants to merge 10 commits from feat/agent-team-confluence-node into main

10 commits

Author SHA1 Message Date
2e985882f7 style(agent-team-web): prettier-format the layout.ts loopback label
The conf_draft loopback-label ternary exceeded printWidth; wrap per prettier.
Formatting only — compiled bundle unchanged (no redeploy).
2026-06-25 12:31:03 -04:00
9bbe48ed12 feat(agent-team): surface the Confluence-writer lane on the dashboard
The dashboard topology is introspected from the compiled graph, but
_maximal_compiled() built it without confluence=True, so conf_draft/conf_gate/
conf_write never appeared on the pipeline map. Wire them in:

- topology.py: _maximal_compiled(confluence=True); NODE_META entries for the
  three conf nodes (own 'docs'/Documentation tree, conf_gate kind=gate,
  gated=True so the UI dims the opt-in lane); add the 'docs' tree; map the
  CONF_* phases in PHASE_TO_NODE so a live task buckets onto its node.
- layout.ts: the conf_gate->conf_draft request-changes loop reads '↺ revised'.

Frontend renders the new lane automatically (generic dagre layout + dynamic
treeByNode). +2 topology tests; edge-classification test updated (intake is now
a conditional branch point: clarify vs conf_draft). Full suite 1616 passed;
frontend typecheck + 44 tests green.
2026-06-25 12:21:40 -04:00
f56ee1c58d harden(agent-team): identity-aware macro-preservation guard
Address the verifier's residual on the macro-loss fix: the guard was raw-count
based, so a body that DROPPED the real Mermaid macro while ADDING an unrelated
macro (equal count) could slip through. Replace count_storage_macros in the
guard with storage_macro_signature (per-ac:name multiset) and refuse if ANY
macro identity loses occurrences. +2 tests.
2026-06-25 11:22:07 -04:00
ed8e9a13ab fix(agent-team): close Confluence macro-loss + harden write authz (security-review)
Resolve the security-review BLOCKER and the confirmed authz/info findings on
the Confluence-writer node:

- BLOCKER (data-loss): _page_has_macros fail-OPENed (real ConfluenceClient has
  no page_has_macros/ADF methods) so every live write fell through to a
  wholesale storage-body PUT that drops Mermaid macros — the page-1540098
  diagram-loss class. Add count_storage_macros + a real ConfluenceClient
  .page_has_macros (storage-body detection); make _page_has_macros FAIL CLOSED;
  and add _assert_macros_preserved as the final backstop: refuse any storage
  write whose body carries fewer macros than the live page.
- AUTHZ-CONF-01: add an opt-in AGENT_TEAM_CONFLUENCE_ALLOWED_PAGE_IDS allowlist
  enforced server-side before a live update (model-derived page_id).
- AUTHZ-CONF-02: stop silently picking the first accessible Confluence site;
  require CONFLUENCE_CLOUD_ID when multiple resolve.
- INFO: 'applied' now fail-honest (defaults False, not True) on a missing attr.

+10 regression tests; full suite 1612 passed; ruff clean.
2026-06-25 11:18:01 -04:00
b16fbd1aaf fix(agent-team-web): scope vitest to src/ so it skips the Playwright e2e specs
ci-web's `test` (vitest run) globbed e2e/dashboard.smoke.spec.ts, which imports
@playwright/test (the Playwright runner, not vitest) and fails to load — "1 test
file failed" while the 44 unit tests passed. Restrict vitest's include to
src/**; Playwright keeps its own testDir (./e2e).
2026-06-25 10:56:48 -04:00
8530074551 ci(agent-team-web): wire Prettier + ESLint + Playwright and enable the CI steps (#65)
- Prettier 3.6.2: .prettierrc.json + .prettierignore + format/format:check
  scripts; formatted the web/ tree.
- ESLint 9 flat config (@eslint/js + typescript-eslint + react-hooks +
  react-refresh, eslint-config-prettier last so Prettier owns formatting);
  lint script; src/components/ui/** opts out of react-refresh (shadcn primitives
  co-export their cva variants). 0 problems.
- Playwright 1.61.1: playwright.config.ts (build+preview webServer) + an e2e
  Board-renders smoke against a mocked /api; test:e2e script. Reusable workflow
  installs chromium itself.
- ci-web.yaml: required-scripts -> format:check,lint,build,test,test:e2e;
  run-format-check/lint/e2e -> true; e2e-browser chromium.

All five scripts verified green locally.
2026-06-25 10:56:48 -04:00
9ee2e3c127 fix(agent-team): clear CodeQL URL-substring alert in confluence client test
The host assertion used `url.startswith("https://seahaven.atlassian.net")`,
which CodeQL flags (incomplete URL substring sanitization — a spoofed host like
`...atlassian.net.evil.com` passes a prefix check). Parse the URL and compare
scheme+netloc exactly instead. (PR #66 review finding.)
2026-06-25 10:56:48 -04:00
0ff0ce2849 test(agent-team): guard that every pending_questions kind has a coordinator delivery branch
Pins the invariant behind the CRITICAL gate-delivery gap this PR fixed: a new
interrupt kind added to the pending_questions CHECK without a coordinator branch
suspends the task forever (no Slack post, no ledger row). The guard parses the
allowed kinds from PENDING_QUESTIONS_DDL (source of truth) and asserts every
non-default kind has a graph.*_KIND constant referenced in coordinator.py.
Negative-checked: removing the confluence_approval branch fails the guard.
2026-06-25 10:56:48 -04:00
e9bf016035 fix(agent-team): align Confluence node with merged #61 lessons + post-review hardening
Apply review findings from mining PR #61/#63/#64 and the recent merged PRs:

- conf_draft_node: drop the agentic invoker config (max_turns=8 + Read/Grep/Glob
  + budget) that merged #61 reverted live (#60); use max_turns=4 tools-off like
  the planner. The draft is a reasoning->JSON node — repo context is folded into
  the prompt, never fetched via Claude tools (feedback_claude_sdk_single_shot).
  Invert the test that pinned the old tools-on contract.
- confluence_writer: record the dedicated Phase.CONF_DRAFT/CONF_GATE/CONF_WRITE
  values instead of borrowing VERIFY/REVIEW/BUILD, so the ledger/transitions/
  dashboard show the real lane.
- invoker: note the allowed_tools seam is replicated from merged #61 (drop on a
  future rebase; byte-equivalent default None->[]).
- dashboard: document that conf_* vertices need no _NODE_TO_STAGE remap (node id
  == stage key, identity fallback resolves them).

Verified (no change needed): coordinator confluence gate shares the plan gate's
crash isolation (_emit never raises, ledger write fail-soft); confluence_* state
channels persist via declared PipelineState channels + checkpointer; Flow B edge
hooks VERIFY's approved terminus, orthogonal to #61's plan-gate-approve routing.

Full suite 1564 passed; ruff + format clean.
2026-06-25 10:56:48 -04:00
c7f9c1bac2 feat(agent-team): Confluence-writer node (draft -> approve gate -> write)
Add a Confluence documentation lane to the Plane-2 pipeline, flag-gated behind
AGENT_TEAM_CONFLUENCE_ENABLED (default off; daemon behavior unchanged when off).

- confluence/client.py: OAuth 2LO + Basic REST client, dry-run-default writes
- confluence/mermaid.py: vendored ADF-only Mermaid editor (macro-count +
  revert-diff guards, dry-run default)
- nodes/confluence_writer.py(+_llm): conf_draft -> conf_gate -> conf_write,
  both direct (task_kind=confluence) and post-build documentation flows
- task_model/graph/coordinator: new phases, state channels, route_after_intake,
  CONFLUENCE_APPROVAL_KIND gate delivery, task_kind forwarding
- db schema v5: widen pending_questions kind CHECK (atomic rebuild)
- tests for client, mermaid, writer node, ledger v5, coordinator gate, e2e
2026-06-25 10:56:48 -04:00