Skip to content

feat: the delivery board projection and the red-pipeline report, plus a CI false-green fix - #14

Merged
sscodeai merged 4 commits into
devfrom
feat/board-and-red-pipeline
Oct 11, 2026
Merged

sscodeai merged 4 commits into
devfrom
feat/board-and-red-pipeline

Conversation

@sscodeai

Copy link
Copy Markdown
Owner

What this is

Three things, in the order they landed. The CI defect was fixed first because a false green in CI is the worst kind of defect this project can ship.

  1. fix(core): a generated pipeline fails when the delivered tests fail — the CI false-green.
  2. feat(core): the delivery board projection (ADR-044) — one row per delivery run.
  3. feat(cli): the board: section — the delivery board reachable from takumi.yaml.

1. The false green in the generated CI workflow (fixed first)

ci_config generates the delivery's OWN pipeline. Its tests job ran the delivered project's test command through a pipe — bash run-tests.sh 2>&1 | tail -60 — so the log stays readable. GitHub runs a run: block with bash -e, which does not set pipefail, so the pipeline's exit status was tail's (always 0): a FAILING suite read GREEN. It was found live, because a deliberately-red delivery passed.

The generated job now emits set -o pipefail in front of the command (renderCiJobScript), for the tests and deliverable jobs and for every dialect, keeping the tail. It is emitted unconditionally rather than sniffed for: a guard that depends on recognising the shape goes blind the day the shape changes.

RED-then-GREEN (packages/core/src/test/ci-config-step.test.ts runs the GENERATED script through bash -e, the shell the host uses):

  • before the fix — not ok 29 - … a FAILING delivered suite must fail the generated github tests job, not read GREEN (got 0)
  • after the fix — non-zero for a failing suite, zero for a passing one, asserted for github, forgejo and gitlab.

Ledger: docs/bugs-fixed.md #49.

2. The delivery board projection (ADR-044)

  • One row per run, keyed by a create marker (renderCreateMarker / createKeyOf): a re-run UPDATES the row it wrote rather than filing a second one.
  • Status follows the pipeline: publish → claimed; green await_checks → pr_open (+ takumi-approved); red await_checks → fix_needed (+ takumi-blocked); merge → merged (+ takumi-merged).
  • Labels are unioned (additive — nothing an earlier step set is cleared).
  • Detail in Takumi State (rich_text): the reader-facing line — which step moved the row, the pull request, the landed SHA.
  • Write-only by construction (ADR-017 rule 1); a projection failure is LOUD and never fatal (ADR-017 rule 2) — a decorative board must not be able to un-accept a delivery the pipeline, the judge and the host already accepted.

3. Real evidence

Red-pipeline report path — one issue + one PR comment per run, read back from sscodeai/takumi-delivery-demo:

Both bodies name await_checks FAILED: 2 check(s) FAILED on 4 with the two failing tests check URLs; the head's check runs are deliverable: success and tests: failure ×2.

Notion rows (delivery board):

  • 3f6c1a02-53fb-81ff-… — red
  • …-8107-… — green
  • …-8164-… — idempotency (a re-run updated the same row; no second row)

Suite

  • pnpm -r build — green
  • pnpm test — 934 tests, 931 pass, 0 fail, 3 skipped
  • pnpm typecheck — green

(Note: two fixture bugs in the new Notion delivery-board test are fixed in the board commit so the suite is green — the simulator keyed its routes on a dashed UUID that normalizeDatabaseId strips, and its read helper ignored the select/status shape the adapter writes to Status.)

…the pull request

The black-box chain reports a RED pipeline by opening ONE issue and commenting on
the pull request (ADR-037), and the engine already does both through the optional
`openIssue`/`commentOnPullRequest` seam. The GitHub adapter did not implement
either, so a real run against GitHub said the honest thing — 'issue: not opened
(the provider cannot record issues)' — and a red pipeline could only name its
failure in the step summary.

- `openIssue` POSTs /repos/{owner}/{repo}/issues and returns the host's number/url.
  Labels are NAMES here (GitHub takes names directly, unlike Forgejo's ids).
- `commentOnPullRequest` POSTs the ISSUE-comment endpoint under the pull request's
  number, because a pull request IS an issue on GitHub's API.
- Both refuse an empty title/number BEFORE any request, and both fail closed
  through the transport when there is no token and no injected seam.

5 tests: the endpoint each call actually hits, the identity returned, the
pre-request refusals, and the no-transport failure. 19/19 in the package.
ci_config generated a tests job that ran the delivered project's command through a pipe (bash run-tests.sh 2>&1 | tail -60) so the log stays readable. GitHub runs a run: block with bash -e, which does NOT set pipefail, so the pipeline's exit status was tail's — always 0 — and a FAILING suite read GREEN. Found live: a deliberately-red delivery passed CI.

The generated job now emits 'set -o pipefail' in front of the command (renderCiJobScript), for the tests AND deliverable jobs and for every dialect, keeping the tail. It is emitted unconditionally rather than sniffed for: a guard that depends on recognising the shape goes blind the day the shape changes.

RED-then-GREEN: packages/core/src/test/ci-config-step.test.ts runs the GENERATED script through 'bash -e' (the shell the host uses) — a failing delivered suite must exit non-zero, a passing one zero, asserted for github, forgejo and gitlab. docs/bugs-fixed.md #49.
The board-projection port: where a black-box run writes its progress for a HUMAN to read. It is its own port, not ctx.board: TaskBoardProvider — a delivery run has no work to find or claim, so handing it one would hand it a capability it must not use. Write-only by construction (ADR-017 rule 1): no read, no claim, no state record, no comment. Injected like ctx.delivery, never discovered; a run with no board projects nowhere and that is not an error.

One delivery = one row, keyed by the run id: a re-run UPDATES the row it wrote rather than filing a second. The engine moves it with the pipeline — publish -> claimed, green await_checks -> pr_open (+ takumi-approved), red await_checks -> fix_needed (+ takumi-blocked), merge -> merged (+ takumi-merged, the landed SHA in the detail). A projection failure is LOUD and NEVER FATAL (ADR-017 rule 2): a decorative board must not be able to un-accept a delivery the pipeline, the judge and the host already accepted.

The Notion adapter is the first implementation: one row per run keyed by core's create marker (renderCreateMarker/createKeyOf), labels unioned (additive, nothing an earlier step set is cleared), the reader-facing detail in the 'Takumi State' rich_text property, and the Status column's type (select vs status) learned from the database before writing.

Tests drive the REAL engine (packages/core/src/test/delivery-board-step.test.ts) and a real in-memory Notion (boards/notion/src/test/notion-delivery-board.test.ts). Two fixture bugs in the latter are fixed here so the suite is green: the simulator keyed its routes on a dashed UUID that normalizeDatabaseId strips, and its read helper ignored the select/status shape the adapter writes to Status.
The engine's board projection was only ever set by a caller writing code — the same gap delivery-command.ts closed for ctx.delivery. Without this a configured black-box run projects nowhere and nothing in a transcript says why, because the engine's own rule is that a run with no board port projects nowhere and that is not an error.

The 'board:' section of takumi.yaml names the DELIVERY board (the write-only row a human reads), NOT the task board — that is the pilot: section's business. It mirrors delivery-command.ts: a per-provider lazy import, a per-provider option list, and a refusal of any option nobody would read (a key the factory drops and a typo are the same silent defect). A credential is never accepted from configuration: the token comes from the environment (NOTION_TOKEN). An invalid section is refused up front, before a run spends its work.

Tests (apps/cli/src/test/board-projection.test.ts) pin the section builder and the option rules: a section with no provider, an unknown provider named with the list, a near-miss key refused with the keys the adapter reads, a credential and a code-only option refused with the reason, and the notion section built from its documented options.
@sscodeai
sscodeai merged commit b9e61de into dev Oct 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant