fix: disinto init must be fully idempotent — safe to re-run on existing factory #239

Closed
opened 2026-04-05 16:53:59 +00:00 by dev-bot · 6 comments
Collaborator

Problem

Running disinto init a second time on an existing factory produces a mix of skips, silent failures, and partial re-creation. It is not safe to re-run for corrective purposes.

Specific gaps:

  • Ops repo existence check uses the configured slug (e.g. disinto-admin/disinto-ops) but the actual repo may be at a different path (e.g. dev-bot/disinto-ops) due to past bugs. The check 404s and init tries to re-create, fails silently.
  • Bot user creation and token generation are not re-entrant — existing users without known passwords can't get new tokens.
  • Label creation, cron installation, and compose generation have some idempotency guards but not all.
  • State file creation (state/.dev-active etc.) is additive only — no reconciliation.

Expected behavior

disinto init should be safe to run at any time on an existing factory. It should:

  1. Check current state before each step and skip if already correct
  2. Fix misconfigurations where possible (e.g. wrong repo namespace, missing tokens)
  3. Never silently swallow errors that leave the factory in an inconsistent state
  4. Report what was skipped, what was fixed, and what failed

Affected files

  • bin/disinto (disinto_init and all setup_* helper functions)

Acceptance criteria

  • Running disinto init <repo-url> twice in a row produces identical results
  • Running it on a partially broken factory fixes what it can and reports what it cannot
  • No silent || true swallowing of errors that indicate broken state
  • Each step logs whether it was skipped (already correct), created, or failed

Dependencies

Depends on #240, #241

CI failure diagnosis (from manual investigation)

The smoke-init CI step fails because the mock Forgejo server (tests/mock-forgejo.py) does not handle the admin repo creation endpoint:

POST /api/v1/admin/users/{username}/repos

The init code now uses this endpoint (from #240 fix) to create the ops repo under disinto-admin. The mock returns 404, init fails with:

Error: failed to create ops repo (HTTP 404)

Fix required

Add a route to tests/mock-forgejo.py that handles POST /api/v1/admin/users/{username}/repos. It should behave like the existing POST /api/v1/user/repos handler but create the repo under the specified username namespace instead of the authenticated user.

Look at the existing mock routes in mock-forgejo.py for the pattern. The response format should match Forgejo's actual response (JSON with id, name, full_name, clone_url, etc.).

How to verify

Run the smoke test locally:

bash tests/smoke-init.sh

Both the ci and smoke-init steps should pass.

## Problem Running `disinto init` a second time on an existing factory produces a mix of skips, silent failures, and partial re-creation. It is not safe to re-run for corrective purposes. Specific gaps: - Ops repo existence check uses the configured slug (e.g. disinto-admin/disinto-ops) but the actual repo may be at a different path (e.g. dev-bot/disinto-ops) due to past bugs. The check 404s and init tries to re-create, fails silently. - Bot user creation and token generation are not re-entrant — existing users without known passwords can't get new tokens. - Label creation, cron installation, and compose generation have some idempotency guards but not all. - State file creation (state/.dev-active etc.) is additive only — no reconciliation. ## Expected behavior `disinto init` should be safe to run at any time on an existing factory. It should: 1. Check current state before each step and skip if already correct 2. Fix misconfigurations where possible (e.g. wrong repo namespace, missing tokens) 3. Never silently swallow errors that leave the factory in an inconsistent state 4. Report what was skipped, what was fixed, and what failed ## Affected files - bin/disinto (disinto_init and all setup_* helper functions) ## Acceptance criteria - [ ] Running `disinto init <repo-url>` twice in a row produces identical results - [ ] Running it on a partially broken factory fixes what it can and reports what it cannot - [ ] No silent `|| true` swallowing of errors that indicate broken state - [ ] Each step logs whether it was skipped (already correct), created, or failed ## Dependencies Depends on #240, #241 ## CI failure diagnosis (from manual investigation) The smoke-init CI step fails because the mock Forgejo server (tests/mock-forgejo.py) does not handle the admin repo creation endpoint: POST /api/v1/admin/users/{username}/repos The init code now uses this endpoint (from #240 fix) to create the ops repo under disinto-admin. The mock returns 404, init fails with: Error: failed to create ops repo (HTTP 404) ### Fix required Add a route to tests/mock-forgejo.py that handles POST /api/v1/admin/users/{username}/repos. It should behave like the existing POST /api/v1/user/repos handler but create the repo under the specified username namespace instead of the authenticated user. Look at the existing mock routes in mock-forgejo.py for the pattern. The response format should match Forgejo's actual response (JSON with id, name, full_name, clone_url, etc.). ### How to verify Run the smoke test locally: bash tests/smoke-init.sh Both the ci and smoke-init steps should pass.
dev-bot added the
backlog
priority
labels 2026-04-05 16:53:59 +00:00
dev-qwen self-assigned this 2026-04-05 18:29:30 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-05 18:29:30 +00:00
Collaborator

Blocked — issue #239

Field Value
Exit reason ci_exhausted
Timestamp 2026-04-05T18:46:05Z
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `ci_exhausted` | | Timestamp | `2026-04-05T18:46:05Z` |
dev-qwen added
blocked
and removed
in-progress
labels 2026-04-05 18:46:05 +00:00
dev-bot added
backlog
and removed
blocked
labels 2026-04-05 19:31:46 +00:00
dev-qwen was unassigned by dev-bot 2026-04-05 19:31:46 +00:00
dev-qwen self-assigned this 2026-04-05 19:44:48 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-05 19:44:48 +00:00
Collaborator

Blocked — issue #239

Field Value
Exit reason no_push
Timestamp 2026-04-05T19:56:43Z
Diagnostic output
Claude did not push branch fix/issue-239
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `no_push` | | Timestamp | `2026-04-05T19:56:43Z` | <details><summary>Diagnostic output</summary> ``` Claude did not push branch fix/issue-239 ``` </details>
dev-qwen added
blocked
and removed
in-progress
labels 2026-04-05 19:56:43 +00:00
dev-bot added
backlog
and removed
blocked
labels 2026-04-05 19:59:45 +00:00
dev-qwen was unassigned by dev-bot 2026-04-05 19:59:45 +00:00
dev-bot self-assigned this 2026-04-05 20:04:02 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-05 20:04:02 +00:00
Author
Collaborator

Blocked — issue #239

Field Value
Exit reason no_push
Timestamp 2026-04-05T20:08:04Z
Diagnostic output
Claude did not push branch fix/issue-239
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `no_push` | | Timestamp | `2026-04-05T20:08:04Z` | <details><summary>Diagnostic output</summary> ``` Claude did not push branch fix/issue-239 ``` </details>
dev-bot added
backlog
and removed
in-progress
labels 2026-04-05 20:08:05 +00:00
dev-bot removed their assignment 2026-04-05 20:17:53 +00:00
dev-bot self-assigned this 2026-04-05 20:19:02 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-05 20:19:03 +00:00
Author
Collaborator

Blocked — issue #239

Field Value
Exit reason no_push
Timestamp 2026-04-05T20:23:23Z
Diagnostic output
Claude did not push branch fix/issue-239
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `no_push` | | Timestamp | `2026-04-05T20:23:23Z` | <details><summary>Diagnostic output</summary> ``` Claude did not push branch fix/issue-239 ``` </details>
dev-bot added
backlog
and removed
in-progress
labels 2026-04-05 20:23:23 +00:00
dev-bot removed their assignment 2026-04-05 20:29:21 +00:00
dev-bot self-assigned this 2026-04-05 20:34:03 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-05 20:34:03 +00:00
Author
Collaborator

Blocked — issue #239

Field Value
Exit reason no_push
Timestamp 2026-04-05T20:37:20Z
Diagnostic output
Claude did not push branch fix/issue-239
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `no_push` | | Timestamp | `2026-04-05T20:37:20Z` | <details><summary>Diagnostic output</summary> ``` Claude did not push branch fix/issue-239 ``` </details>
dev-bot added
blocked
and removed
in-progress
labels 2026-04-05 20:37:21 +00:00
disinto-admin removed the
priority
label 2026-04-05 20:47:51 +00:00
dev-bot added
backlog
and removed
blocked
labels 2026-04-05 20:47:53 +00:00
dev-bot removed their assignment 2026-04-05 20:47:53 +00:00
dev-qwen self-assigned this 2026-04-05 21:02:41 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-05 21:02:42 +00:00
Collaborator

Blocked — issue #239

Field Value
Exit reason review_exhausted
Timestamp 2026-04-05T21:54:37Z
### Blocked — issue #239 | Field | Value | |---|---| | Exit reason | `review_exhausted` | | Timestamp | `2026-04-05T21:54:37Z` |
dev-qwen 2026-04-05 21:54:37 +00:00
  • closed this issue
  • added the
    blocked
    label
dev-qwen removed their assignment 2026-04-05 22:12:49 +00:00
dev-qwen removed the
in-progress
label 2026-04-05 22:12:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#239
No description provided.