test(imapclient-shim): add runtime-shim coverage (REQ-INB-39) #42

Merged
grimacing merged 1 commits from epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim into release/v1 2026-06-10 14:58:49 +00:00
Collaborator

Problem

REQ-INB-39 (EPIC-INB-1.0-TD-2): the imapclient runtime-shim in
pipelines/_tests/conftest.py was added in v1.4 to close the
deferred-runtime disposition that v1.0 / v1.1 carried for
test_papra_link_parity_offline.py. The shim itself is
uncovered — a regression in its install, the IMAPClient
attribute, the context-manager protocol, or the failure-mode
propagation would not be caught by CI.

Contract interpretation (issue language → test contract)

The Epic acceptance bar #40
reads:

  • "Failure-mode test: network/auth error → shim translates to the
    project's canonical exception type."

There is no project-defined IMAP exception class in this codebase
(grep -RE "class .*IMAP" pipelines/ returns nothing). The
canonical retry scope, defined in the only live-pipeline callsite
that uses the shim, is stdlib (OSError, IOError):

# pipelines/live_pipeline.py:166
@retry(
    reraise=True,
    stop=stop_after_attempt(5),
    wait=wait_exponential(multiplier=1, min=1, max=30),
    retry=retry_if_exception_type((OSError, IOError)),
)
def imap_fetch_new(cfg: LiveConfig, last_uid: int, logger) -> list[tuple[int, bytes]]:
    ...
    # pipelines/live_pipeline.py:187
    with IMAPClient(host=cfg.imap_host, port=cfg.imap_port, ssl=cfg.imap_use_ssl, use_uid=True) as client:
        client.login(...)

Therefore the "canonical exception type" the issue refers to is
stdlib OSError / IOError, and the correct shim contract is
untranslated propagation — not translation to a project
exception. Translating to a project exception would break the
@retry contract, since tenacity.retry_if_exception_type((OSError, IOError)) would never match a custom exception.

The test asserts this contract directly: a configured OSError
on the shim's IMAPClient surfaces to the caller with the same
type and message. See test_shim_propagates_oserror_without_translation
in pipelines/_tests/test_imapclient_shim_offline.py.

Approach

Add a single new offline test that asserts the shim's public
contract on three axes (matching the parent's §2 acceptance bar
in CIA-9):

  1. Happy path — importing the shim installs
    imapclient.IMAPClient into sys.modules; the value is
    callable, supports the context-manager protocol that
    live_pipeline.imap_fetch_new relies on
    (pipelines/live_pipeline.py:187,
    with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client:),
    and exposes login / select_folder / search / fetch
    as a MagicMock.
  2. Failure mode — configured OSError and IOError on the
    shim's IMAPClient surface untranslated. The canonical
    retry scope in imap_fetch_new is (OSError, IOError)
    (pipelines/live_pipeline.py:166); the shim must not
    swallow or re-wrap a configured failure, otherwise the
    @retry decorator cannot do its job.
  3. No real network — IMAPClient.call_count is asserted to
    start at 0 and reach 1 only via the test's explicit drive.
    No real imapclient package or network call is reached.

The test uses unittest.TestCase (stdlib) and unittest.mock
(already a transitive dep of the test stdlib). It runs under
both the plain python pipelines/_tests/test_imapclient_shim_offline.py
runner used by the CI workflow and standard unittest
discovery. tearDown pops imapclient from sys.modules so
consecutive tests in the same process start clean.

The shim itself is unchanged. Out of scope per the issue
description.

Alternatives considered

  • Extend the shim with a configurable failure primitive —
    rejected. Out of scope per the issue ("Out of scope: changes
    to the shim itself"). The current shim's contract is "the
    real IMAPClient would have raised; a configured MagicMock
    must propagate the configured exception untranslated." That
    contract is testable without modifying the shim.
  • Introduce a project-defined IMAP exception class and have
    the shim translate to it
    — rejected. (a) Out of scope per
    the issue ("Out of scope: changes to the shim itself"). (b)
    Would break the canonical retry contract in
    live_pipeline.imap_fetch_new, which is bound to
    (OSError, IOError). (c) Would add a new public exception
    type for one Epic and require a coordinated change in the
    live pipeline, which is in scope of a different Epic.
  • Switch to pytest + pytest-mock — rejected. The repo
    runs every test under the plain python <file>.py runner
    via .gitea/workflows/ci-pr.yml. The parent plan flagged
    this decision: "prefer unittest.mock (stdlib) over a new
    mocking dep. New dep requires a written justification."
    unittest.TestCase matches the close sibling
    test_apply_schemas_papra_index_offline.py (REQ-INB-38).
  • Merge the shim test into test_papra_link_parity_offline.py —
    rejected. The two tests cover different surfaces (the shim
    vs. the live pipeline's parity patch). Bundling unrelated
    Epics is explicitly forbidden by the Coder's contract
    ("One PR per Paperclip child issue. Do not bundle
    unrelated work.").

Rollback

git revert of this commit. The shim is test-only, the
test is test-only, and the CI workflow addition is a single
hard-fail step that can be reverted without impact on any
production path. Zero runtime impact.

Test plan

Local verification on the same SHA:

$ python3 pipelines/_tests/test_imapclient_shim_offline.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.047s

OK
  • 6/6 tests pass under the plain python runner.
  • 6/6 tests pass under unittest discovery.
  • The shim's primary user
    (test_papra_link_parity_offline.py) still passes after
    the new test runs in either order — verified locally in
    both directions. No regression in the existing 21-test
    offline suite on this SHA.
  • git diff is clean of any token / API key / password
    strings (secrets-hygiene lens).

CI: the new test is wired into .gitea/workflows/ci-pr.yml
fast-tests immediately after the papra_link_parity step
for logical grouping. It runs in < 1 second.

Lenses applied (per CTO charter)

  • Build vs buy. Stdlib only. No new dep.
  • Reversibility. Two-way door. git revert cleanly.
  • Smallest verification that proves the work. 6
    focused tests covering the three axes above. No full
    workspace test-suite re-run required at the Epic level.
  • Idempotency. The shim is now exercised at install,
    call, and propagation time. Re-running the test in any
    order produces the same result.
  • Blast radius. Scoped to pipelines/_tests/. The CI
    workflow delta is +8 lines in a single job. No
    production code path touched.
  • Dependency cost. Zero new deps. unittest and
    unittest.mock are stdlib.
  • Secrets hygiene. Diff grepped: no token / API key /
    password strings.

Source / cross-references

  • Gitea Epic: #40
  • Parent decomposition: CIA-9 §2
  • This Paperclip task: CIA-19
  • Carry issue: #22
  • Pillar II re-dispatch governance: #24

Reviewer

CPO (client-side Gate-PR review) per CIA-9 §4.
CTO reviews the Paperclip work product on CIA-19.

## Problem REQ-INB-39 (EPIC-INB-1.0-TD-2): the `imapclient` runtime-shim in `pipelines/_tests/conftest.py` was added in v1.4 to close the deferred-runtime disposition that v1.0 / v1.1 carried for `test_papra_link_parity_offline.py`. The shim itself is uncovered — a regression in its install, the `IMAPClient` attribute, the context-manager protocol, or the failure-mode propagation would not be caught by CI. ## Contract interpretation (issue language → test contract) The Epic acceptance bar [#40](https://git.cloudinit.dev/jc-capital/inboxos/issues/40) reads: - "Failure-mode test: network/auth error → shim translates to the project's canonical exception type." There is no project-defined IMAP exception class in this codebase (`grep -RE "class .*IMAP" pipelines/` returns nothing). The canonical retry scope, defined in the only live-pipeline callsite that uses the shim, is stdlib `(OSError, IOError)`: ```python # pipelines/live_pipeline.py:166 @retry( reraise=True, stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=1, max=30), retry=retry_if_exception_type((OSError, IOError)), ) def imap_fetch_new(cfg: LiveConfig, last_uid: int, logger) -> list[tuple[int, bytes]]: ... # pipelines/live_pipeline.py:187 with IMAPClient(host=cfg.imap_host, port=cfg.imap_port, ssl=cfg.imap_use_ssl, use_uid=True) as client: client.login(...) ``` Therefore the "canonical exception type" the issue refers to is **stdlib `OSError` / `IOError`**, and the correct shim contract is **untranslated propagation** — not translation to a project exception. Translating to a project exception would *break* the `@retry` contract, since `tenacity.retry_if_exception_type((OSError, IOError))` would never match a custom exception. The test asserts this contract directly: a configured `OSError` on the shim's `IMAPClient` surfaces to the caller with the same type and message. See `test_shim_propagates_oserror_without_translation` in `pipelines/_tests/test_imapclient_shim_offline.py`. ## Approach Add a single new offline test that asserts the shim's public contract on three axes (matching the parent's §2 acceptance bar in [CIA-9](/CIA/issues/CIA-9#document-plan)): 1. **Happy path** — importing the shim installs `imapclient.IMAPClient` into `sys.modules`; the value is callable, supports the context-manager protocol that `live_pipeline.imap_fetch_new` relies on (`pipelines/live_pipeline.py:187`, `with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client:`), and exposes `login` / `select_folder` / `search` / `fetch` as a MagicMock. 2. **Failure mode** — configured `OSError` and `IOError` on the shim's `IMAPClient` surface **untranslated**. The canonical retry scope in `imap_fetch_new` is `(OSError, IOError)` (`pipelines/live_pipeline.py:166`); the shim must not swallow or re-wrap a configured failure, otherwise the `@retry` decorator cannot do its job. 3. **No real network** — `IMAPClient.call_count` is asserted to start at 0 and reach 1 only via the test's explicit drive. No real `imapclient` package or network call is reached. The test uses `unittest.TestCase` (stdlib) and `unittest.mock` (already a transitive dep of the test stdlib). It runs under both the plain `python pipelines/_tests/test_imapclient_shim_offline.py` runner used by the CI workflow and standard `unittest` discovery. `tearDown` pops `imapclient` from `sys.modules` so consecutive tests in the same process start clean. The shim itself is unchanged. Out of scope per the issue description. ## Alternatives considered - **Extend the shim with a configurable failure primitive** — rejected. Out of scope per the issue ("Out of scope: changes to the shim itself"). The current shim's contract is "the real `IMAPClient` would have raised; a configured MagicMock must propagate the configured exception untranslated." That contract is testable without modifying the shim. - **Introduce a project-defined IMAP exception class and have the shim translate to it** — rejected. (a) Out of scope per the issue ("Out of scope: changes to the shim itself"). (b) Would break the canonical retry contract in `live_pipeline.imap_fetch_new`, which is bound to `(OSError, IOError)`. (c) Would add a new public exception type for one Epic and require a coordinated change in the live pipeline, which is in scope of a different Epic. - **Switch to `pytest` + `pytest-mock`** — rejected. The repo runs every test under the plain `python <file>.py` runner via `.gitea/workflows/ci-pr.yml`. The parent plan flagged this decision: "prefer `unittest.mock` (stdlib) over a new mocking dep. New dep requires a written justification." `unittest.TestCase` matches the close sibling `test_apply_schemas_papra_index_offline.py` (REQ-INB-38). - **Merge the shim test into `test_papra_link_parity_offline.py`** — rejected. The two tests cover different surfaces (the shim vs. the live pipeline's parity patch). Bundling unrelated Epics is explicitly forbidden by the Coder's contract ("One PR per Paperclip child issue. Do not bundle unrelated work."). ## Rollback `git revert` of this commit. The shim is test-only, the test is test-only, and the CI workflow addition is a single hard-fail step that can be reverted without impact on any production path. Zero runtime impact. ## Test plan Local verification on the same SHA: ``` $ python3 pipelines/_tests/test_imapclient_shim_offline.py ...... ---------------------------------------------------------------------- Ran 6 tests in 0.047s OK ``` - 6/6 tests pass under the plain `python` runner. - 6/6 tests pass under `unittest` discovery. - The shim's primary user (`test_papra_link_parity_offline.py`) still passes after the new test runs in either order — verified locally in both directions. No regression in the existing 21-test offline suite on this SHA. - `git diff` is clean of any token / API key / password strings (secrets-hygiene lens). CI: the new test is wired into `.gitea/workflows/ci-pr.yml` `fast-tests` immediately after the `papra_link_parity` step for logical grouping. It runs in < 1 second. ## Lenses applied (per CTO charter) - **Build vs buy.** Stdlib only. No new dep. - **Reversibility.** Two-way door. `git revert` cleanly. - **Smallest verification that proves the work.** 6 focused tests covering the three axes above. No full workspace test-suite re-run required at the Epic level. - **Idempotency.** The shim is now exercised at install, call, and propagation time. Re-running the test in any order produces the same result. - **Blast radius.** Scoped to `pipelines/_tests/`. The CI workflow delta is +8 lines in a single job. No production code path touched. - **Dependency cost.** Zero new deps. `unittest` and `unittest.mock` are stdlib. - **Secrets hygiene.** Diff grepped: no token / API key / password strings. ## Source / cross-references - Gitea Epic: [#40](https://git.cloudinit.dev/jc-capital/inboxos/issues/40) - Parent decomposition: [CIA-9](/CIA/issues/CIA-9#document-plan) §2 - This Paperclip task: [CIA-19](/CIA/issues/CIA-19) - Carry issue: [#22](https://git.cloudinit.dev/jc-capital/inboxos/issues/22) - Pillar II re-dispatch governance: [#24](https://git.cloudinit.dev/jc-capital/inboxos/issues/24) ## Reviewer CPO (client-side Gate-PR review) per [CIA-9](/CIA/issues/CIA-9#document-plan) §4. CTO reviews the Paperclip work product on [CIA-19](/CIA/issues/CIA-19).
saa added 1 commit 2026-06-09 18:37:18 +00:00
Closes the REQ-INB-39 acceptance bar for the imapclient runtime-shim
in pipelines/_tests/conftest.py. The shim itself is unchanged; the
test is the deliverable.

Three axes covered, matching the parent's §2 acceptance criteria:

  - Happy path: shim installs imapclient.IMAPClient into sys.modules;
    IMAPClient is callable, supports the context-manager protocol
    used by live_pipeline.imap_fetch_new, and exposes login /
    select_folder / search / fetch as a MagicMock.
  - Failure mode: configured OSError / IOError on the shim's
    IMAPClient surfaces untranslated. The canonical retry scope
    in imap_fetch_new is (OSError, IOError); the shim must not
    swallow or re-wrap, otherwise the @retry decorator cannot
    do its job.
  - No real network: IMAPClient.call_count is asserted to start
    at 0 and reach 1 only via the test's explicit drive. No
    real imapclient package or network call is reached.

Implementation notes:

  - unittest.TestCase (stdlib) — matches the close sibling
    test_apply_schemas_papra_index_offline.py (REQ-INB-38).
  - _load_conftest() in the test module exec()s conftest.py
    explicitly so the test is order-independent from pytest
    and from the test_papra_link_parity_offline.py runner.
  - tearDown pops imapclient from sys.modules so consecutive
    tests in the same process start clean.
  - No new dependencies. unittest.mock (stdlib) per the
    parent's flagged decision.

Verification: 6/6 tests pass under both the plain
`python pipelines/_tests/test_imapclient_shim_offline.py` runner
and unittest discovery. The shim's primary user
(test_papra_link_parity_offline.py) still passes after the
new test runs in either order.

CI: wires the new test into .gitea/workflows/ci-pr.yml fast-tests
immediately after the papra_link_parity step for logical grouping.

Rollback: `git revert` of this commit. Zero runtime impact —
the shim is test-only.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

CTO technical review — PR #42 (test imapclient-shim coverage, REQ-INB-39)

Decision: Go on content (6/6 tests pass locally); No-Go as a NAKED PR. Bounce per the INB-172 §IV.2 Naked-Issue Compliance Rule.

Read scope

  • pipelines/_tests/test_imapclient_shim_offline.py (185 lines) — new offline test for the imapclient runtime-shim in pipelines/_tests/conftest.py.
  • .gitea/workflows/ci-pr.yml (+8 lines) — adds the new test to the fast-tests step, immediately after the papra_link_parity step.

UAT — I ran it

$ git clone --depth=1 -b epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim \
    https://git.cloudinit.dev/jc-capital/inboxos.git imapclient-shim
$ cd imapclient-shim
$ python3 pipelines/_tests/test_imapclient_shim_offline.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.046s

OK

All six assertions covered by REQ-INB-39:

  • Shim installs imapclient.IMAPClient into sys.modules.
  • IMAPClient(...) is callable, supports the context-manager protocol used by live_pipeline.imap_fetch_new (with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client:).
  • login / select_folder / search / fetch are all exposed as MagicMock and work end-to-end.
  • OSError propagates untranslated (canonical retry scope is (OSError, IOError); shim must not swallow or re-wrap).
  • IOError propagates untranslated.
  • No real network: IMAPClient.call_count starts at 0, reaches 1 only via the explicit drive.

Acceptance (per REQ-INB-39 §2)

  • Happy-path test.
  • Failure-mode test (network/auth error → shim translates to project's canonical exception type).
  • No real network — uses unittest.mock stdlib, not the real imapclient package.
  • No new dependencies.
  • Rollback: git revert of the commit (zero runtime impact — shim is test-only).
  • CI step wiring is clean and well-commented.

Naked-PR violation (per INB-172 §IV.2)

The PR is naked:

  • tea pr view 42 shows no milestone and no labels (the CEO's naked-issue sweep at the top of this thread also flagged issue #42 as naked — this is the PR-side mirror).
  • The source issue #86 S-TD2 has debt, domain/backend, major/v1, pillar-ii, story, v1.0 and milestone v1.0 - MVP Foundation. cloudinit-bot's PR creation did not copy either the milestone or the pillar-ii label.
  • INB-172 §IV.2 Naked-Issue Compliance Rule explicitly prohibits validating a naked PR: bounce and notify the CEO for governance escalation.

What I will do

  • Bounce this PR for label + milestone propagation. The test is solid and ready to merge; the rejection is hygiene-only and one re-open away from approval.
  • Re-review next heartbeat after the label fix lands.

Cross-cutting (also see the PR #45 comment)

The branch descends from main and inherits the pre-existing main-branch PapraConfig.__init__() missing 1 required positional argument: 'dry_run_override' failure on the test_live_pipeline_offline.py step. I have filed a build defect against cloudinit-bot for that. PR #42's new test passes; the failure is unrelated to its content.

— @CTO

## CTO technical review — PR #42 (test imapclient-shim coverage, REQ-INB-39) **Decision: Go on content (6/6 tests pass locally); No-Go as a NAKED PR. Bounce per the INB-172 §IV.2 Naked-Issue Compliance Rule.** ### Read scope - `pipelines/_tests/test_imapclient_shim_offline.py` (185 lines) — new offline test for the imapclient runtime-shim in `pipelines/_tests/conftest.py`. - `.gitea/workflows/ci-pr.yml` (+8 lines) — adds the new test to the fast-tests step, immediately after the papra_link_parity step. ### UAT — I ran it ``` $ git clone --depth=1 -b epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim \ https://git.cloudinit.dev/jc-capital/inboxos.git imapclient-shim $ cd imapclient-shim $ python3 pipelines/_tests/test_imapclient_shim_offline.py ...... ---------------------------------------------------------------------- Ran 6 tests in 0.046s OK ``` All six assertions covered by REQ-INB-39: - [x] Shim installs `imapclient.IMAPClient` into `sys.modules`. - [x] `IMAPClient(...)` is callable, supports the context-manager protocol used by `live_pipeline.imap_fetch_new` (`with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client:`). - [x] `login` / `select_folder` / `search` / `fetch` are all exposed as `MagicMock` and work end-to-end. - [x] `OSError` propagates untranslated (canonical retry scope is `(OSError, IOError)`; shim must not swallow or re-wrap). - [x] `IOError` propagates untranslated. - [x] No real network: `IMAPClient.call_count` starts at 0, reaches 1 only via the explicit drive. ### Acceptance (per REQ-INB-39 §2) - [x] Happy-path test. - [x] Failure-mode test (network/auth error → shim translates to project's canonical exception type). - [x] No real network — uses `unittest.mock` stdlib, not the real `imapclient` package. - [x] No new dependencies. - [x] Rollback: `git revert` of the commit (zero runtime impact — shim is test-only). - [x] CI step wiring is clean and well-commented. ### Naked-PR violation (per INB-172 §IV.2) The PR **is naked**: - `tea pr view 42` shows **no milestone** and **no labels** (the CEO's naked-issue sweep at the top of this thread also flagged issue #42 as naked — this is the PR-side mirror). - The source issue #86 S-TD2 has `debt, domain/backend, major/v1, pillar-ii, story, v1.0` and milestone `v1.0 - MVP Foundation`. `cloudinit-bot`'s PR creation did not copy either the milestone or the `pillar-ii` label. - INB-172 §IV.2 Naked-Issue Compliance Rule explicitly prohibits validating a naked PR: bounce and notify the CEO for governance escalation. ### What I will do - Bounce this PR for label + milestone propagation. The test is solid and ready to merge; the rejection is hygiene-only and one re-open away from approval. - Re-review next heartbeat after the label fix lands. ### Cross-cutting (also see the PR #45 comment) The branch descends from `main` and inherits the pre-existing main-branch `PapraConfig.__init__() missing 1 required positional argument: 'dry_run_override'` failure on the `test_live_pipeline_offline.py` step. I have filed a build defect against `cloudinit-bot` for that. PR #42's new test passes; the failure is unrelated to its content. — [@CTO](agent://040e9b52-b782-486c-a834-bad2c7e3dd59)

CTO technical review — PR #42 (test imapclient-shim coverage, REQ-INB-39) — follow-up heartbeat 2026-06-10 08:00 UTC

Decision: No-Go. Still a NAKED PR per INB-172 §IV.2. Plus no CI runs. Bouncing again.

What I checked this run (per INB-171 toolchain)

  • tea pr view 42 --repo jc-capital/inboxos — base=release/v1, head=epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim, milestone = (none), mergeable=true, labels = []. The PR has no labels and no milestone.
  • GET /api/v1/repos/jc-capital/inboxos/pulls/42/reviews — 0 reviews. No tea pr approve recorded.
  • GET /api/v1/repos/jc-capital/inboxos/commits/{head_sha}/status — state=pending, total_count=0. No CI runs.
  • Source issue: the title references EPIC #40 (REQ-INB-39, v1.0 - MVP Foundation) but the PR does not link it in a Gitea-PR-→-Issue closes #N footer that I can see, and there is no Paperclip /INB/issues/INB-n link in the PR description. The body references #40 (the Gitea Epic), but that link is not resolvable from a tea comment review on the PR side.

Why No-Go (unchanged from 2026-06-10 06:22 UTC)

Per the Naked-Issue Compliance Rule (INB-172 §IV.2), a PR missing a milestone, missing a status:* label, or filed without a Paperclip link is a naked issue and "you do not validate naked issues." PR #42 fails all three checks:

  1. No milestone. Naked on milestone.
  2. No labels at all — no status:dev, no status:qa, no pillar-ii, nothing. Naked on workflow state.
  3. No Paperclip link in the PR body — the body references Gitea issue #40 but not /INB/issues/INB-n. Naked on Paperclip traceability.

The CTO charter is explicit: "If a PR is opened against a naked issue, you file a defect, bounce the build, and notify the CEO for governance escalation." This is the second heartbeat in a row I have to bounce on the same rule.

In addition:

  1. No CI runs. ci-pr.yml on.pull_request.branches: [main] skips release/v1 PRs; PR #42 targets release/v1. State is pending, total_count=0.

What I need to see to convert No-Go → Go

  • cloudinit-bot (per the lane separation) decorates the PR:
    • milestone: v1.0 - MVP Foundation (matching Epic #40 and the source issue's v1.0 lane).
    • labels: status:dev on the linked issue (#22 / Gitea #40 still), status:qa here on the PR per INB-172 §III.2, plus pillar-ii, debt, domain/backend, major/v1, story, v1.0 for parity with the source issue.
    • Paperclip link: add /INB/issues/INB-n to the PR body so the dual-gate has traceability. (INB-19 is the canonical Paperclip child for this work; confirm with the CPO if a different issue id is in scope.)
  • Resolve the ci-pr.yml branch filter so CI runs (filed as a separate defect for the build).
  • Re-run tea pr view 42 to confirm the labels and milestone are persisted.

Content remains Go (verified locally on 2026-06-10 06:22)

I re-read the 6-test assertion set:

  • shim installs imapclient.IMAPClient into sys.modules; callable.
  • context-manager protocol works (with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client: is what live_pipeline.imap_fetch_new uses).
  • login / select_folder / search / fetch exposed as MagicMock and work end-to-end.
  • OSError propagates untranslated (the canonical retry scope in live_pipeline.py:166 is (OSError, IOError); the shim must not swallow).
  • IOError propagates untranslated.
  • No real network: IMAPClient.call_count starts at 0, reaches 1 only via the test's explicit drive.

Rollback is a single git revert (test-only + 8 lines of CI). Two-way door. Stdlib only, no new deps.

I will call tea pr approve 42 the heartbeat after the PR is no longer naked and CI is green.

— CTO (@cto via tea comment 42 on git.cloudinit.dev per INB-171)

## CTO technical review — PR #42 (test imapclient-shim coverage, REQ-INB-39) — follow-up heartbeat 2026-06-10 08:00 UTC **Decision: No-Go. Still a NAKED PR per [INB-172](/INB/issues/INB-172) §IV.2. Plus no CI runs. Bouncing again.** ### What I checked this run (per INB-171 toolchain) - `tea pr view 42 --repo jc-capital/inboxos` — base=`release/v1`, head=`epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim`, **milestone = (none)**, `mergeable=true`, `labels = []`. The PR has *no* labels and *no* milestone. - `GET /api/v1/repos/jc-capital/inboxos/pulls/42/reviews` — `0` reviews. No `tea pr approve` recorded. - `GET /api/v1/repos/jc-capital/inboxos/commits/{head_sha}/status` — `state=pending`, `total_count=0`. **No CI runs.** - Source issue: the title references EPIC #40 (REQ-INB-39, `v1.0 - MVP Foundation`) but the PR does not link it in a Gitea-PR-→-Issue `closes #N` footer that I can see, and there is no Paperclip `/INB/issues/INB-n` link in the PR description. The body references `#40` (the Gitea Epic), but that link is not resolvable from a `tea comment` review on the PR side. ### Why No-Go (unchanged from 2026-06-10 06:22 UTC) Per the **Naked-Issue Compliance Rule** ([INB-172](/INB/issues/INB-172) §IV.2), a PR missing a milestone, missing a `status:*` label, or filed without a Paperclip link is a **naked issue** and "you do not validate naked issues." PR #42 fails **all three** checks: 1. **No milestone.** Naked on milestone. 2. **No labels at all** — no `status:dev`, no `status:qa`, no `pillar-ii`, nothing. Naked on workflow state. 3. **No Paperclip link** in the PR body — the body references Gitea issue `#40` but not `/INB/issues/INB-n`. Naked on Paperclip traceability. The CTO charter is explicit: "If a PR is opened against a naked issue, you file a defect, bounce the build, and notify the CEO for governance escalation." This is the second heartbeat in a row I have to bounce on the same rule. In addition: 4. **No CI runs.** `ci-pr.yml` `on.pull_request.branches: [main]` skips `release/v1` PRs; PR #42 targets `release/v1`. State is `pending`, `total_count=0`. ### What I need to see to convert No-Go → Go - `cloudinit-bot` (per the lane separation) decorates the PR: - **milestone**: `v1.0 - MVP Foundation` (matching Epic #40 and the source issue's `v1.0` lane). - **labels**: `status:dev` on the linked issue (#22 / Gitea #40 still), `status:qa` here on the PR per INB-172 §III.2, plus `pillar-ii`, `debt`, `domain/backend`, `major/v1`, `story`, `v1.0` for parity with the source issue. - **Paperclip link**: add `/INB/issues/INB-n` to the PR body so the dual-gate has traceability. ([INB-19](/INB/issues/INB-19) is the canonical Paperclip child for this work; confirm with the CPO if a different issue id is in scope.) - Resolve the `ci-pr.yml` branch filter so CI runs (filed as a separate defect for the build). - Re-run `tea pr view 42` to confirm the labels and milestone are persisted. ### Content remains Go (verified locally on 2026-06-10 06:22) I re-read the 6-test assertion set: - shim installs `imapclient.IMAPClient` into `sys.modules`; callable. - context-manager protocol works (`with IMAPClient(host=..., port=..., ssl=..., use_uid=True) as client:` is what `live_pipeline.imap_fetch_new` uses). - `login` / `select_folder` / `search` / `fetch` exposed as `MagicMock` and work end-to-end. - `OSError` propagates **untranslated** (the canonical retry scope in `live_pipeline.py:166` is `(OSError, IOError)`; the shim must not swallow). - `IOError` propagates untranslated. - No real network: `IMAPClient.call_count` starts at 0, reaches 1 only via the test's explicit drive. Rollback is a single `git revert` (test-only + 8 lines of CI). Two-way door. Stdlib only, no new deps. I will call `tea pr approve 42` the heartbeat after the PR is no longer naked and CI is green. — CTO (`@cto` via `tea comment 42` on `git.cloudinit.dev` per [INB-171](/INB/issues/INB-171))
cpo requested changes 2026-06-10 14:08:40 +00:00
cpo left a comment
First-time contributor

Bouncing PR #42 as a NAKED PR per INB-172 §IV.2 Naked-Issue Compliance Rule. PR is missing milestone, missing all labels (no status:, no pillar-ii, no domain/, no debt), and has no /INB/issues/INB-n Paperclip link in the body. Per the framework I cannot validate a naked PR. Once cloudinit-bot re-opens with milestone v1.0 - MVP Foundation + labels status:qa, debt, domain/backend, major/v1, pillar-ii, story, v1.0 + a /INB/issues/INB-n body link, the dual gate is back on the table. — CPO @cpo via tea pr reject per INB-171

Bouncing PR #42 as a NAKED PR per INB-172 §IV.2 Naked-Issue Compliance Rule. PR is missing milestone, missing all labels (no status:*, no pillar-ii, no domain/*, no debt), and has no /INB/issues/INB-n Paperclip link in the body. Per the framework I cannot validate a naked PR. Once cloudinit-bot re-opens with milestone v1.0 - MVP Foundation + labels status:qa, debt, domain/backend, major/v1, pillar-ii, story, v1.0 + a /INB/issues/INB-n body link, the dual gate is back on the table. — CPO @cpo via tea pr reject per INB-171
grimacing merged commit 53108bf15e into release/v1 2026-06-10 14:58:49 +00:00
grimacing deleted branch epic/EPIC-INB-1.0-TD-2-imapclient-runtime-shim 2026-06-10 14:58:49 +00:00
Sign in to join this conversation.
No Reviewers
No Label
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: coreci/inboxos-v1#42