Compare commits
10 commits
40a638ea37
...
eb817e9539
| Author | SHA1 | Date | |
|---|---|---|---|
| eb817e9539 | |||
| 401e7f9f6b | |||
| 8d4f9ad587 | |||
| dc341d7cc6 | |||
| 7aae00e933 | |||
| 36a3030f78 | |||
| acaef67f4d | |||
| effe568e07 | |||
| a858daf90a | |||
| 5a1c1ca629 |
23 changed files with 649 additions and 99 deletions
20
.claude/rules/agents.md
Normal file
20
.claude/rules/agents.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
## Kaizen Agents
|
||||
|
||||
Specialized agent personas available on demand via the state-hub MCP.
|
||||
|
||||
**Discover:** `list_kaizen_agents()` — returns all agents with name, description, category
|
||||
**Load:** `get_kaizen_agent("tdd-workflow")` — returns full instructions; read and follow them
|
||||
|
||||
Common agents:
|
||||
|
||||
| Agent | Category | When to use |
|
||||
|-------|----------|-------------|
|
||||
| `tdd-workflow` | testing | Step-by-step TDD8 workflow for any feature |
|
||||
| `code-refactoring` | quality | Code quality analysis and safe refactoring |
|
||||
| `test-maintenance` | testing | Diagnose and fix failing tests |
|
||||
| `requirements-engineering` | process | Prevent interface/mock mismatches upfront |
|
||||
| `keepaTodofile` | process | Maintain TODO.md during work |
|
||||
| `project-management` | process | Track status, determine next steps |
|
||||
| `datamodel-optimization` | quality | Optimize dataclasses and data structures |
|
||||
|
||||
All 17 agents: call `list_kaizen_agents()` for the full list.
|
||||
8
.claude/rules/architecture.md
Normal file
8
.claude/rules/architecture.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
## Architecture
|
||||
|
||||
<!-- TODO: Describe the key design decisions and component structure.
|
||||
Key modules, data flows, external integrations, state machines, etc. -->
|
||||
|
||||
## Quick Reference
|
||||
|
||||
`~/state-hub/mcp_server/TOOLS.md` — MCP tool reference
|
||||
|
|
@ -20,7 +20,7 @@ Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run wa
|
|||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=railiance-forge` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **still** use `warden route` for credential ownership |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||
|
||||
### Quick routing table
|
||||
|
|
|
|||
42
.claude/rules/first-session.md
Normal file
42
.claude/rules/first-session.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
## First Session Protocol
|
||||
|
||||
Triggered when `get_domain_summary("financials")` shows **no workplans**.
|
||||
The project is registered but work has not yet been structured.
|
||||
|
||||
**Step 1 — Read, don't write**
|
||||
- `~/the-custodian/canon/projects/financials/project_charter_v0.1.md` — purpose, scope
|
||||
- `~/the-custodian/canon/projects/financials/roadmap_v0.1.md` — planned phases
|
||||
- Scan repo root: README, directory structure, existing code or docs
|
||||
|
||||
**Step 2 — Survey in-progress work**
|
||||
Look for TODOs, open branches, half-finished files. Note done vs. started but incomplete.
|
||||
|
||||
**Step 3 — Propose workplans to Bernd**
|
||||
Propose 1–3 workplans — each a coherent strand, weeks to months, anchored to a
|
||||
roadmap phase. **Wait for approval before creating.**
|
||||
|
||||
**Step 4 — Write the workplan file; fix-consistency registers it (ADR-001)**
|
||||
```
|
||||
workplans/RAILIANCE-WP-NNNN-<slug>.md ← write this, commit it
|
||||
```
|
||||
Then register by running the consistency check — do **not** call
|
||||
`create_workplan`/`create_task` (or legacy `create_workstream`) yourself;
|
||||
manual registration duplicates what C-06 creates from the file:
|
||||
```bash
|
||||
statehub fix-consistency --repo railiance-forge
|
||||
```
|
||||
C-06 creates the hub workplan + tasks and writes `state_hub_workstream_id` /
|
||||
`state_hub_task_id` back into the file (legacy field names, kept for
|
||||
compatibility — they hold workplan/task IDs).
|
||||
|
||||
**Step 5 — Record the setup**
|
||||
```
|
||||
add_progress_event(
|
||||
summary="First session: structured financials into N workplans, M tasks",
|
||||
event_type="milestone",
|
||||
topic_id="ca369340-a64e-442e-98f1-a4fa7dc74a38",
|
||||
detail={"workplans": [...], "tasks_created": M}
|
||||
)
|
||||
```
|
||||
|
||||
<!-- Delete or archive this file once past first session -->
|
||||
8
.claude/rules/repo-boundary.md
Normal file
8
.claude/rules/repo-boundary.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
## Repo boundary
|
||||
|
||||
This repo owns **railiance-forge** only. It does not own:
|
||||
|
||||
<!-- TODO: List what belongs in adjacent repos, e.g.:
|
||||
- SSH key management → railiance-infra/
|
||||
- State hub code → state-hub/
|
||||
-->
|
||||
5
.claude/rules/repo-identity.md
Normal file
5
.claude/rules/repo-identity.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
**Purpose:** Railiance forge and artifact infrastructure: current Gitea operation, future Forgejo migration, container/package registries, runner substrate, artifact lifecycle, and forge operating evidence.
|
||||
|
||||
**Domain:** financials
|
||||
**Repo slug:** railiance-forge
|
||||
**Topic ID:** ca369340-a64e-442e-98f1-a4fa7dc74a38
|
||||
94
.claude/rules/session-protocol.md
Normal file
94
.claude/rules/session-protocol.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
## Session Protocol
|
||||
|
||||
Dev Hub (State Hub API): http://127.0.0.1:8000
|
||||
MCP server name in `~/.claude.json`: `dev-hub`
|
||||
|
||||
**Step 1 — Orient**
|
||||
|
||||
Read the offline-safe brief first — it works without a live hub connection:
|
||||
```bash
|
||||
cat .custodian-brief.md
|
||||
```
|
||||
Then call the MCP tool for richer cross-domain context when MCP tools are exposed:
|
||||
```
|
||||
get_domain_summary("financials")
|
||||
```
|
||||
If MCP tools are unavailable in the current agent session, use the REST API:
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8000/state/summary" | python3 -m json.tool
|
||||
```
|
||||
If the hub is offline: `cd ~/state-hub && make api`
|
||||
|
||||
**Step 2 — Check inbox**
|
||||
With MCP tools:
|
||||
```
|
||||
get_messages(to_agent="railiance-forge", unread_only=True)
|
||||
```
|
||||
Mark read with `mark_message_read(message_id)`. Reply or act on coordination
|
||||
requests before proceeding.
|
||||
|
||||
Without MCP tools:
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=railiance-forge&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
**Step 3 — Scan workplans**
|
||||
```bash
|
||||
ls workplans/
|
||||
```
|
||||
For each file with `status: ready`, `active`, or `blocked`, note pending
|
||||
`wait`/`todo`/`progress` tasks.
|
||||
|
||||
**Step 4 — Present brief**
|
||||
|
||||
1. **Active workplans** for `financials` — title, task counts, blocking decisions
|
||||
2. **Pending tasks** from `workplans/` + any `[repo:railiance-forge]` hub tasks
|
||||
3. **Goal guidance** — if `goal_guidance` in summary:
|
||||
- `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"*
|
||||
- `alignment_warnings`: flag if active work is not aligned with current goal
|
||||
4. **Suggested next action** — highest-priority open item
|
||||
5. **SBOM status** — flag if `last_sbom_at` is unset for this repo
|
||||
|
||||
If no workplans: follow First Session Protocol (`first-session.md`).
|
||||
|
||||
**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()`
|
||||
|
||||
> State Hub is a *read model*. **Never register workplans or tasks by hand**
|
||||
> (`create_workplan`, `create_task`, or the legacy `create_workstream`) — write
|
||||
> the workplan file in `workplans/` and run `fix-consistency`; its C-06 check
|
||||
> registers the workplan and its tasks in the hub and writes the IDs back into
|
||||
> the file. Manual registration creates duplicates the moment fix-consistency
|
||||
> runs. Work structure belongs in repo files (ADR-001).
|
||||
>
|
||||
> Terminology: "workstream" is the legacy name for workplan. Some API/frontmatter
|
||||
> field names keep it for compatibility (`state_hub_workstream_id`,
|
||||
> `workstream_id` params) — treat them as workplan IDs.
|
||||
|
||||
**Session close:**
|
||||
With MCP tools:
|
||||
```
|
||||
add_progress_event(summary="...", topic_id="ca369340-a64e-442e-98f1-a4fa7dc74a38", workplan_id="<uuid>")
|
||||
```
|
||||
Without MCP tools:
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"topic_id":"ca369340-a64e-442e-98f1-a4fa7dc74a38","workplan_id":"<uuid>","event_type":"note","summary":"what changed","author":"codex"}'
|
||||
```
|
||||
If workplan files were modified, ensure the local copy is up to date first,
|
||||
then sync from the repo checkout:
|
||||
```bash
|
||||
git pull --ff-only
|
||||
statehub fix-consistency
|
||||
```
|
||||
For repos where implementation runs on a remote machine (e.g. CoulombCore),
|
||||
use the pull-before-fix mode from any shell with the State Hub CLI:
|
||||
```bash
|
||||
statehub fix-consistency --repo railiance-forge --remote
|
||||
```
|
||||
**C-15** (DB task ahead of file) is normal in multi-machine workflows — writeback
|
||||
will sync the file to match DB. **C-16** (repo behind remote) blocks all writes
|
||||
until you pull — intentional to prevent clobbering remote progress.
|
||||
18
.claude/rules/stack-and-commands.md
Normal file
18
.claude/rules/stack-and-commands.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
## Stack
|
||||
|
||||
- **Language:** Helm values + Kubernetes manifests, Bash Make targets
|
||||
- **Key deps:** Gitea (current forge runtime), Actions runner substrate, SOPS/age; runs against the S2 cluster on railiance01
|
||||
|
||||
## Dev Commands
|
||||
|
||||
```bash
|
||||
make help # list all targets
|
||||
make check-tools && make check-sops # preflight (read-only)
|
||||
make gitea-status # forge runtime + database state (read-only)
|
||||
make runner-status # Actions runner / registry probes (read-only)
|
||||
make registry-docs | evidence-docs | runner-docs # print canonical docs
|
||||
make gitea-deploy | gitea-ingress-deploy # production-touching
|
||||
```
|
||||
|
||||
`gitea-deploy` / `gitea-ingress-deploy` change the production forge —
|
||||
operator approval required. Status and docs targets are read-only.
|
||||
45
.claude/rules/workplan-convention.md
Normal file
45
.claude/rules/workplan-convention.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
## Workplan Convention (ADR-001)
|
||||
|
||||
File location: `workplans/RAILIANCE-WP-NNNN-<slug>.md`
|
||||
ID prefix: `RAILIANCE-WP-`
|
||||
|
||||
Work items originate as files in this repo **before** being registered in the hub.
|
||||
|
||||
Canonical workplan frontmatter statuses are:
|
||||
`proposed`, `ready`, `active`, `blocked`, `backlog`, `finished`, `archived`.
|
||||
Use `proposed` for a newly drafted plan, `ready` after review against current
|
||||
repo state, and `finished` when implementation is complete. `stalled` and
|
||||
`needs_review` are derived health labels, not stored statuses.
|
||||
|
||||
Closed workplans may be moved to `workplans/archived/` with a completion-date
|
||||
prefix: `YYMMDD-RAILIANCE-WP-NNNN-<slug>.md`. The frontmatter id remains
|
||||
unchanged; the prefix is only for quick visual reference.
|
||||
|
||||
Small opportunistic tasks discovered during another session use **Ad Hoc Tasks**:
|
||||
`workplans/ADHOC-YYYY-MM-DD.md`, workplan slug `adhoc-YYYY-MM-DD`, and task ids
|
||||
`ADHOC-YYYY-MM-DD-T01`, `T02`, etc. Use adhocs only for low-risk work completed
|
||||
directly. Promote anything requiring analysis, design, approval, dependencies, or
|
||||
multiple planned phases into a normal workplan.
|
||||
|
||||
Ecosystem todos from other agents arrive as `[repo:railiance-forge]` hub tasks —
|
||||
visible at session start. Pick one up by creating the workplan file, committing,
|
||||
and running `statehub fix-consistency` — C-06 registers the workplan in the hub.
|
||||
Never register by hand with `create_workplan`/`create_workstream`.
|
||||
|
||||
Task blocks use this shape:
|
||||
|
||||
```task
|
||||
id: RAILIANCE-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
```
|
||||
|
||||
Status progression is `todo` → `progress` → `done`; use `wait` for waiting or
|
||||
blocked work and `cancel` for stopped work.
|
||||
|
||||
Workplan frontmatter carries `state_hub_workstream_id` — a legacy field name
|
||||
kept for compatibility ("workstream" is the old term for workplan); it holds
|
||||
the hub workplan id and is written by fix-consistency. Do not edit or rename it.
|
||||
|
||||
<!-- Ralph Loop rules and HEUREKA sequence: ~/.claude/CLAUDE.md — do not duplicate here -->
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
|
||||
# Custodian Brief — railiance-forge
|
||||
|
||||
**Domain:** railiance
|
||||
**Last synced:** 2026-06-08 08:14 UTC
|
||||
**Domain:** financials
|
||||
**Last synced:** 2026-07-01 22:24 UTC
|
||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||
|
||||
## Active Workstreams
|
||||
|
|
@ -13,6 +13,6 @@
|
|||
## MCP Orientation (when available)
|
||||
|
||||
If the state-hub MCP server is reachable, call:
|
||||
`get_domain_summary("railiance")`
|
||||
`get_domain_summary("financials")`
|
||||
This provides richer cross-domain context.
|
||||
If the MCP call fails, use this file as your orientation source.
|
||||
|
|
|
|||
29
.forgejo/workflows/ci-smoke.yaml
Normal file
29
.forgejo/workflows/ci-smoke.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Canonical CI smoke template (tier 1 routing drill).
|
||||
# Copy to: .forgejo/workflows/ci-smoke.yaml in consumer repos.
|
||||
name: CI Smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
host-smoke:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Routing probe (host runner)
|
||||
run: |
|
||||
set -eu
|
||||
echo "repository=${GITHUB_REPOSITORY:-unknown}"
|
||||
echo "sha=${GITHUB_SHA:-unknown}"
|
||||
echo "runner=${RUNNER_NAME:-unknown}"
|
||||
uname -a
|
||||
|
||||
container-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Routing probe (container label)
|
||||
run: |
|
||||
set -eu
|
||||
echo "container-smoke ok for ${GITHUB_REPOSITORY:-unknown}"
|
||||
156
AGENTS.md
156
AGENTS.md
|
|
@ -1,48 +1,54 @@
|
|||
# railiance-forge - Agent Instructions
|
||||
# railiance-forge — Agent Instructions
|
||||
|
||||
## Repo Identity
|
||||
|
||||
**Purpose:** Railiance forge and artifact infrastructure - source forge runtime,
|
||||
container/package registries, forge-backed runner substrate, artifact lifecycle,
|
||||
and future Forgejo migration.
|
||||
**Purpose:** Railiance forge and artifact infrastructure: current Gitea operation, future Forgejo migration, container/package registries, runner substrate, artifact lifecycle, and forge operating evidence.
|
||||
|
||||
**Domain:** railiance
|
||||
**Domain:** financials
|
||||
**Repo slug:** railiance-forge
|
||||
**Topic ID:** `ca369340-a64e-442e-98f1-a4fa7dc74a38`
|
||||
**Workplan prefix:** `FORGE-WP-`
|
||||
**Workplan prefix:** `RAILIANCE-WP-`
|
||||
|
||||
---
|
||||
|
||||
## State Hub Integration
|
||||
|
||||
The Custodian State Hub tracks work across all domains. Interact via HTTP REST -
|
||||
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
|
||||
there is no MCP server for Codex agents.
|
||||
|
||||
| Context | URL |
|
||||
|---------|-----|
|
||||
| Local workstation | `http://127.0.0.1:8000` |
|
||||
| Remote via tunnel | `http://127.0.0.1:18000` |
|
||||
| Optional local edge relay | http://127.0.0.1:18080 |
|
||||
|
||||
When an operator has enabled the edge relay, set API_BASE to the relay URL.
|
||||
Queueable writes return an explicit queued receipt if the central hub is
|
||||
unreachable. Treat that as pending local evidence, then ask the operator to run
|
||||
statehub outbox status/replay after connectivity returns.
|
||||
|
||||
### Orient at session start
|
||||
|
||||
```bash
|
||||
# Offline brief — works without hub connection
|
||||
cat .custodian-brief.md
|
||||
|
||||
curl -s "http://127.0.0.1:8000/workstreams/?topic_id=ca369340-a64e-442e-98f1-a4fa7dc74a38&status=active" \
|
||||
# Active workplans for this domain
|
||||
curl -s "http://127.0.0.1:8000/workplans/?topic_id=ca369340-a64e-442e-98f1-a4fa7dc74a38&status=active" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# Check inbox
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=railiance-forge&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
Mark a message read:
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
### Log progress at session close
|
||||
### Log progress (required at session close)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
|
|
@ -51,12 +57,12 @@ curl -s -X POST http://127.0.0.1:8000/progress/ \
|
|||
"summary": "what was done",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workstream_id": "<uuid>",
|
||||
"workplan_id": "<uuid>",
|
||||
"task_id": "<uuid>"
|
||||
}'
|
||||
```
|
||||
|
||||
Omit `workstream_id` / `task_id` when not applicable.
|
||||
Omit `workplan_id` / `task_id` when not applicable.
|
||||
|
||||
### Update task status
|
||||
|
||||
|
|
@ -64,10 +70,9 @@ Omit `workstream_id` / `task_id` when not applicable.
|
|||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "progress"}'
|
||||
# values: wait | todo | progress | done | cancel
|
||||
```
|
||||
|
||||
Canonical task values: `wait | todo | progress | done | cancel`.
|
||||
|
||||
### Flag a task for human review
|
||||
|
||||
```bash
|
||||
|
|
@ -81,46 +86,24 @@ curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
|||
## Session Protocol
|
||||
|
||||
**Start:**
|
||||
|
||||
1. `cat .custodian-brief.md` - domain goal and open workstreams.
|
||||
2. Check inbox:
|
||||
`GET /messages/?to_agent=railiance-forge&unread_only=true`; mark read.
|
||||
3. Scan workplans: `ls workplans/` and note `ready`, `active`, or `blocked`
|
||||
workplans and open tasks.
|
||||
4. Check blocked tasks: `GET /tasks/?needs_human=true`.
|
||||
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
|
||||
2. Check inbox: `GET /messages/?to_agent=railiance-forge&unread_only=true`; mark read
|
||||
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||
|
||||
**During work:**
|
||||
|
||||
- Update task statuses in workplan files as tasks progress.
|
||||
- Record significant decisions via `POST /decisions/`.
|
||||
- Keep forge responsibilities separate from S4 templates and S5 app releases.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
This repo currently contains documentation, workplans, and read-only operator
|
||||
entry points. There is no app build, package install, or unit-test command yet.
|
||||
|
||||
Use these checks for ordinary edits:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
make registry-docs
|
||||
make check-tools
|
||||
make -C /home/worsch/state-hub fix-consistency REPO=railiance-forge
|
||||
```
|
||||
|
||||
`make gitea-status` is read-only but requires a kubeconfig pointed at a
|
||||
representative Railiance cluster.
|
||||
- Update task statuses in workplan files as tasks progress
|
||||
- Record significant decisions via `POST /decisions/`
|
||||
|
||||
**Close:**
|
||||
|
||||
1. Update workplan file task statuses.
|
||||
2. Log progress with `POST /progress/`.
|
||||
3. After workplan file changes, run from `~/state-hub`:
|
||||
|
||||
1. Update workplan file task statuses to reflect progress
|
||||
2. Log: `POST /progress/` with a summary of what changed
|
||||
3. After workplan file changes, run:
|
||||
```bash
|
||||
make fix-consistency REPO=railiance-forge
|
||||
statehub fix-consistency
|
||||
```
|
||||
Coding agents should run this directly; ask the operator only if the CLI or
|
||||
State Hub API is unavailable. This syncs task status from files into the hub DB.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -146,7 +129,7 @@ Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run wa
|
|||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=railiance-forge` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **still** use `warden route` for credential ownership |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||
|
||||
### Quick routing table
|
||||
|
|
@ -174,70 +157,69 @@ every repo's agent instructions because it is high-frequency, high-risk, and eas
|
|||
get wrong.
|
||||
|
||||
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||
|
||||
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||
<!-- Append repo-specific agent instructions below this marker.
|
||||
The state-hub template sync preserves content after this line. -->
|
||||
|
||||
---
|
||||
|
||||
## Workplan Convention
|
||||
## Workplan Convention (ADR-001)
|
||||
|
||||
Work items originate as files in this repo. State Hub indexes those files as
|
||||
workstreams and task blocks.
|
||||
Work items originate as files in this repo — not in the hub. The hub is a
|
||||
read/cache/index layer that rebuilds from files.
|
||||
|
||||
**File location:** `workplans/FORGE-WP-NNNN-<slug>.md`
|
||||
**File location:** `workplans/RAILIANCE-WP-NNNN-<slug>.md`
|
||||
|
||||
**Archived location:** `workplans/archived/YYMMDD-FORGE-WP-NNNN-<slug>.md`
|
||||
**Archived location:** finished workplans may move to
|
||||
`workplans/archived/YYMMDD-RAILIANCE-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||
the completion/archive date; the frontmatter `id` does not change.
|
||||
|
||||
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
|
||||
this only for low-risk work completed directly; create a normal workplan for
|
||||
anything needing analysis, design, approval, dependencies, or multiple phases.
|
||||
|
||||
**Frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: FORGE-WP-NNNN
|
||||
id: RAILIANCE-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||
owner: codex
|
||||
topic_slug: railiance
|
||||
topic_slug: ...
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
state_hub_workstream_id: "<uuid>" # written by fix-consistency - do not edit
|
||||
state_hub_workstream_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
---
|
||||
```
|
||||
|
||||
**Task block format:**
|
||||
Use `proposed` for a new draft, `ready` after review against current repo
|
||||
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||
derived health labels, not frontmatter statuses.
|
||||
|
||||
````text
|
||||
**Task block format** (one per `##` section):
|
||||
|
||||
```
|
||||
## Task Title
|
||||
|
||||
```task
|
||||
id: FORGE-WP-NNNN-T01
|
||||
status: todo | progress | done | wait | cancel
|
||||
` ` `task
|
||||
id: RAILIANCE-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency - do not edit
|
||||
```
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
` ` `
|
||||
|
||||
Task description text.
|
||||
````
|
||||
```
|
||||
|
||||
Status progression: `todo` -> `progress` -> `done`, or `wait` / `cancel`.
|
||||
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||
|
||||
To create a new workplan:
|
||||
|
||||
1. Write the file following the format above.
|
||||
2. Run from `~/state-hub`:
|
||||
|
||||
```bash
|
||||
make fix-consistency REPO=railiance-forge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Repository Boundaries
|
||||
|
||||
This repo owns forge runtime and artifact infrastructure. It does not own:
|
||||
|
||||
- OS/host provisioning (`railiance-infra`);
|
||||
- Kubernetes runtime primitives (`railiance-cluster`);
|
||||
- shared database/storage/secret platforms (`railiance-platform`);
|
||||
- generic CI/CD templates and developer portal paths (`railiance-enablement`);
|
||||
- user-facing application releases (`railiance-apps`);
|
||||
- source application code.
|
||||
1. Write the file following the format above
|
||||
2. Notify the custodian operator to run `make fix-consistency REPO=railiance-forge`
|
||||
(or send a message to the hub agent via `POST /messages/`)
|
||||
|
|
|
|||
12
CLAUDE.md
Normal file
12
CLAUDE.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# railiance-forge — Claude Code Instructions
|
||||
|
||||
@SCOPE.md
|
||||
@.claude/rules/repo-identity.md
|
||||
@.claude/rules/session-protocol.md
|
||||
@.claude/rules/first-session.md
|
||||
@.claude/rules/workplan-convention.md
|
||||
@.claude/rules/stack-and-commands.md
|
||||
@.claude/rules/architecture.md
|
||||
@.claude/rules/repo-boundary.md
|
||||
@.claude/rules/credential-routing.md
|
||||
@.claude/rules/agents.md
|
||||
5
Makefile
5
Makefile
|
|
@ -59,6 +59,9 @@ runner-docs: ## Print Gitea Actions runner substrate docs and evidence
|
|||
runner-status: ## Read-only Actions runner, host, and inter-hub registry probes
|
||||
bash tools/gitea-runner-status.sh
|
||||
|
||||
forgejo-runner-status: ## Read-only Forgejo Actions runner and endpoint probes
|
||||
bash tools/forgejo-runner-status.sh
|
||||
|
||||
check-runner-tools: ## Check local tools used by runner inspection targets
|
||||
@missing=0; \
|
||||
for tool in curl ssh docker; do \
|
||||
|
|
@ -109,4 +112,4 @@ help: ## Show this help
|
|||
/^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \
|
||||
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
|
||||
|
||||
.PHONY: check-tools check-sops registry-docs evidence-docs runner-docs runner-status check-runner-tools gitea-deploy gitea-ingress-deploy gitea-status help
|
||||
.PHONY: check-tools check-sops registry-docs evidence-docs runner-docs runner-status forgejo-runner-status check-runner-tools gitea-deploy gitea-ingress-deploy gitea-status help
|
||||
|
|
|
|||
80
docs/forgejo-actions-runner-substrate.md
Normal file
80
docs/forgejo-actions-runner-substrate.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Forgejo Actions Runner Substrate
|
||||
|
||||
Last reviewed: 2026-07-03
|
||||
|
||||
Status: **in-cluster runner on railiance01** (ADR-004). Supersedes interim coulombcore
|
||||
host runner.
|
||||
|
||||
## Purpose
|
||||
|
||||
Forge-owned Actions runner for `https://forgejo.coulomb.social` so `coulomb`
|
||||
organization repos build and publish without workstation or legacy-host Docker.
|
||||
Label contract aligns with `docs/gitea-actions-runner-substrate.md` and
|
||||
`docs/ci-runner-actions-gitops-ownership.md`.
|
||||
|
||||
## Production Runner (ADR-004)
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Runner name | `railiance01-build-01` |
|
||||
| Scope | `coulomb` organization |
|
||||
| Placement | `forgejo` namespace, railiance01 k3s |
|
||||
| Manifest | `railiance-apps/manifests/forgejo-runner.yaml` |
|
||||
| Images | `code.forgejo.org/forgejo/runner:6.3.1`, `docker:27-dind` |
|
||||
| Job runtime | DinD sidecar (`DOCKER_HOST=tcp://127.0.0.1:2375`) |
|
||||
| State PVC | `forgejo-runner-data` |
|
||||
| Cluster deploy authority | Not included |
|
||||
|
||||
### Labels
|
||||
|
||||
```text
|
||||
self-hosted:host,linux:host,linux_amd64:host,container-build:host,registry-publish:host,railiance01:host,ubuntu-latest:docker://node:20-bookworm,docker:docker://node:20-bookworm
|
||||
```
|
||||
|
||||
## Operator Commands
|
||||
|
||||
```bash
|
||||
# S5 deploy (railiance-apps)
|
||||
cd ~/railiance-apps
|
||||
make forgejo-runner-registration-deploy # after SOPS encrypt
|
||||
make forgejo-runner-deploy
|
||||
make forgejo-runner-status
|
||||
|
||||
# Endpoint smoke
|
||||
make forgejo-smoke
|
||||
|
||||
# Generate registration token (in cluster; do not log token)
|
||||
KUBECONFIG=~/.kube/config-hosteurope kubectl exec -n forgejo deploy/forgejo-gitea -- \
|
||||
forgejo actions generate-runner-token --scope coulomb
|
||||
```
|
||||
|
||||
## Secret Boundaries
|
||||
|
||||
- `forgejo-runner-registration` Kubernetes Secret — registration token only.
|
||||
- Repo/org registry credentials — Forgejo Actions secrets, not in Git.
|
||||
- No kubeconfig or OpenBao tokens on the runner pod by default.
|
||||
|
||||
## Retired: Interim coulombcore Host Runner
|
||||
|
||||
`railiance-coulombcore-build-01` on coulombcore was a scheduling proof only.
|
||||
Disable after in-cluster runner is healthy:
|
||||
|
||||
```bash
|
||||
ssh coulombcore 'sudo systemctl disable --now forgejo-runner'
|
||||
```
|
||||
|
||||
## Evidence
|
||||
|
||||
- Host probe (2026-07-03): `coulomb/forgejo-actions-probe` workflow `success`.
|
||||
- In-cluster (2026-07-03): `railiance01-build-01` 2/2 Ready; `container-probe`
|
||||
and `image-build` workflows `success`. Image-build uses static docker-cli on
|
||||
the non-root runner host + `DOCKER_HOST=tcp://127.0.0.1:2375` (DinD); org
|
||||
secrets `REGISTRY_USER` / `REGISTRY_TOKEN`; pushes to
|
||||
`forgejo.coulomb.social/coulomb/forgejo-actions-probe`.
|
||||
- `actions/checkout@v4` failed on the host runner — use `git clone` in job steps
|
||||
until checkout action compatibility is confirmed.
|
||||
|
||||
## References
|
||||
|
||||
- `railiance-infra/docs/adr/ADR-004-forgejo-in-cluster-actions-runner.md`
|
||||
- [Forgejo runner installation](https://forgejo.org/docs/v11.0/admin/actions/runner-installation/)
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
id: capability.railiance.forge-infrastructure
|
||||
name: Railiance Forge And Artifact Infrastructure
|
||||
summary: Source forge, registry, and automation-runner infrastructure for Railiance, separated out from
|
||||
railiance-apps/railiance-enablement; covers current Gitea operation, the Forgejo migration, container/package
|
||||
registries, and Actions runner substrate.
|
||||
owner: railiance-forge
|
||||
status: draft
|
||||
domain: financials
|
||||
tags:
|
||||
- railiance
|
||||
- forgejo
|
||||
- forge
|
||||
- ci-runner
|
||||
maturity:
|
||||
discovery:
|
||||
current: D3
|
||||
target: D5
|
||||
confidence: medium
|
||||
rationale: README and SCOPE.md explicitly name 'current Gitea operation, future Forgejo migration,
|
||||
container and package registries, Actions runner substrate, artifact lifecycle' as the scope; status
|
||||
noted as 'active forge extraction' with canonical registry operation docs already landed.
|
||||
availability:
|
||||
current: A1
|
||||
target: A3
|
||||
confidence: medium
|
||||
rationale: README explicitly states 'no live Helm deploy or Kubernetes apply was run as part of the
|
||||
move' — documentation and operator targets exist, but this repo is mid-extraction; has .gitea CI
|
||||
(not yet migrated to .forgejo itself, notably, despite owning the migration).
|
||||
external_evidence:
|
||||
completeness:
|
||||
level: C1
|
||||
confidence: low
|
||||
basis: scope_vs_intent_and_consumer_expectations
|
||||
satisfied_expectations:
|
||||
- scope separation from railiance-apps/railiance-enablement documented
|
||||
- canonical registry operation docs and runner ownership contracts
|
||||
broken_expectations: []
|
||||
out_of_scope_expectations: []
|
||||
reliability:
|
||||
level: R0
|
||||
confidence: low
|
||||
basis: consumer_quality_signals
|
||||
known_reliability_risks:
|
||||
- repo is actively being founded/extracted; no live deploy yet by its own account
|
||||
- directly relevant to REUSE-WP-0019 but hasn't migrated its own CI to .forgejo/ yet
|
||||
discovery:
|
||||
intent: Own source forge, artifact registry, and CI runner substrate responsibilities for Railiance,
|
||||
separated from application (railiance-apps) and enablement (railiance-enablement) concerns, across
|
||||
the Gitea-to-Forgejo transition.
|
||||
includes:
|
||||
- forge operation (current Gitea, migrating to Forgejo)
|
||||
- container/package registries
|
||||
- Actions runner substrate
|
||||
- artifact lifecycle
|
||||
excludes:
|
||||
- application workload deployment (railiance-apps)
|
||||
- CI/CD templates for consuming repos (railiance-enablement)
|
||||
assumptions: []
|
||||
use_cases: []
|
||||
research_memos: []
|
||||
availability:
|
||||
current_level: A1
|
||||
target_level: A3
|
||||
current_artifacts:
|
||||
- operator docs and tools/ (extraction in progress)
|
||||
target_artifacts: []
|
||||
consumption_modes:
|
||||
- cli (operator tooling)
|
||||
- documentation/runbooks
|
||||
relations:
|
||||
depends_on: []
|
||||
supports: []
|
||||
related_to: []
|
||||
evidence:
|
||||
documentation:
|
||||
- README.md
|
||||
- SCOPE.md
|
||||
tests: []
|
||||
consumer_feedback: []
|
||||
bug_reports: []
|
||||
incidents: []
|
||||
consumer_guidance:
|
||||
recommended_for:
|
||||
- Railiance operators managing forge/registry/runner infrastructure, especially during the Gitea-to-Forgejo
|
||||
transition
|
||||
not_recommended_for:
|
||||
- treating this as production-stable — the repo's own README states the extraction is active and no
|
||||
live deploy has run yet
|
||||
known_limitations:
|
||||
- actively being founded; no live Helm/k8s deploy has run as part of the extraction yet; own CI still
|
||||
under .gitea/ pending its own Forgejo migration
|
||||
promotion_history: []
|
||||
---
|
||||
|
||||
# Railiance Forge And Artifact Infrastructure
|
||||
|
||||
## Overview
|
||||
|
||||
`railiance-forge` is being founded to separate source forge, artifact registry, and CI/Actions runner responsibilities from `railiance-apps` and `railiance-enablement`. It directly owns the current Gitea operation and the future Forgejo migration — of particular relevance to REUSE-WP-0019 — though by its own account no live deploy has run yet.
|
||||
|
||||
## Assessment notes
|
||||
|
||||
### Discovery
|
||||
|
||||
README and SCOPE.md explicitly name 'current Gitea operation, future Forgejo migration, container and package registries, Actions runner substrate, artifact lifecycle' as the scope; status noted as 'active forge extraction' with canonical registry operation docs already landed.
|
||||
|
||||
### Availability
|
||||
|
||||
README explicitly states 'no live Helm deploy or Kubernetes apply was run as part of the move' — documentation and operator targets exist, but this repo is mid-extraction; has .gitea CI (not yet migrated to .forgejo itself, notably, despite owning the migration).
|
||||
|
||||
### Completeness
|
||||
|
||||
First-pass honest assessment from the REUSE-WP-0017 coverage campaign
|
||||
(reuse-surface). No external consumer feedback exists yet; levels reflect
|
||||
scope-vs-intent documentation quality, not internal code quality.
|
||||
|
||||
### Reliability
|
||||
|
||||
No production consumer telemetry exists yet; reliability level is
|
||||
intentionally conservative pending REUSE-WP-0019 reuse-telemetry evidence.
|
||||
|
||||
## Promotion checklist
|
||||
|
||||
- [x] ID follows `capability.<domain>.<name>` pattern
|
||||
- [x] Maturity enums match `specs/CapabilityMaturityStandard.md`
|
||||
- [x] `external_evidence` is populated separately from `maturity`
|
||||
- [ ] Relations reference valid capability IDs (none yet)
|
||||
- [x] Index entry added in `registry/indexes/capabilities.yaml`
|
||||
|
|
@ -1,4 +1,22 @@
|
|||
version: 1
|
||||
updated: '2026-06-16'
|
||||
updated: '2026-07-06'
|
||||
domain: helix_forge
|
||||
capabilities: []
|
||||
capabilities:
|
||||
- id: capability.railiance.forge-infrastructure
|
||||
name: Railiance Forge And Artifact Infrastructure
|
||||
summary: Source forge, registry, and automation-runner infrastructure for Railiance, separated out from
|
||||
railiance-apps/railiance-enablement; covers current Gitea operation, the Forgejo migration, container/package
|
||||
registries, and Actions runner substrate.
|
||||
vector: D3 / A1 / C1 / R0
|
||||
domain: financials
|
||||
status: draft
|
||||
owner: railiance-forge
|
||||
path: registry/capabilities/capability.railiance.forge-infrastructure.md
|
||||
tags:
|
||||
- railiance
|
||||
- forgejo
|
||||
- forge
|
||||
- ci-runner
|
||||
consumption_modes:
|
||||
- cli (operator tooling)
|
||||
- documentation/runbooks
|
||||
|
|
|
|||
57
tools/forgejo-runner-status.sh
Executable file
57
tools/forgejo-runner-status.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env bash
|
||||
set -u
|
||||
|
||||
FORGEJO_URL="${FORGEJO_URL:-https://forgejo.coulomb.social}"
|
||||
FORGEJO_KUBECONFIG="${FORGEJO_KUBECONFIG:-${HOME}/.kube/config-hosteurope}"
|
||||
RUNNER_HOST="${RUNNER_HOST:-}"
|
||||
RUNNER_SSH_USER="${RUNNER_SSH_USER:-}"
|
||||
RUNNER_SSH_KEY="${RUNNER_SSH_KEY:-}"
|
||||
SSH_CONNECT_TIMEOUT="${SSH_CONNECT_TIMEOUT:-5}"
|
||||
|
||||
section() { printf '\n## %s\n' "$1"; }
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
http_code() { curl -sS -m 8 -o /dev/null -w '%{http_code}' "$1" 2>/dev/null || printf 'error'; }
|
||||
|
||||
section "Tool availability"
|
||||
for tool in curl ssh; do
|
||||
if have "$tool"; then printf 'ok: %s\n' "$tool"; else printf 'missing: %s\n' "$tool"; fi
|
||||
done
|
||||
|
||||
section "Forgejo endpoint checks"
|
||||
if have curl; then
|
||||
printf 'forgejo root: %s\n' "$(http_code "${FORGEJO_URL}/")"
|
||||
printf 'forgejo api version: %s\n' "$(http_code "${FORGEJO_URL}/api/v1/version")"
|
||||
printf 'forgejo registry /v2 (GET): %s\n' "$(curl -sS -m 8 -o /dev/null -w '%{http_code}' -X GET "${FORGEJO_URL}/v2/")"
|
||||
else
|
||||
echo "curl missing; skipping endpoint checks"
|
||||
fi
|
||||
|
||||
section "In-cluster runner (railiance01)"
|
||||
if have kubectl && [ -r "${FORGEJO_KUBECONFIG}" ]; then
|
||||
KUBECONFIG="${FORGEJO_KUBECONFIG}" kubectl get deploy,pods,pvc -n forgejo -l app.kubernetes.io/name=forgejo-runner --ignore-not-found 2>/dev/null || true
|
||||
KUBECONFIG="${FORGEJO_KUBECONFIG}" kubectl logs -n forgejo deploy/forgejo-runner -c runner --tail=5 2>/dev/null || true
|
||||
else
|
||||
echo "kubectl or kubeconfig missing; skipping in-cluster probe"
|
||||
fi
|
||||
|
||||
section "Legacy host runner probe (optional)"
|
||||
if [ -n "${RUNNER_HOST}" ] && have ssh; then
|
||||
ssh_target="${RUNNER_HOST}"
|
||||
if [ -n "${RUNNER_SSH_USER}" ]; then ssh_target="${RUNNER_SSH_USER}@${RUNNER_HOST}"; fi
|
||||
ssh_args=(-o BatchMode=yes -o ConnectTimeout="${SSH_CONNECT_TIMEOUT}")
|
||||
if [ -n "${RUNNER_SSH_KEY}" ]; then ssh_args+=(-i "${RUNNER_SSH_KEY}"); fi
|
||||
ssh "${ssh_args[@]}" "${ssh_target}" '
|
||||
set -u
|
||||
echo "host=$(hostname)"
|
||||
if command -v forgejo-runner >/dev/null 2>&1; then forgejo-runner -v || true; else echo "missing: forgejo-runner"; fi
|
||||
if command -v docker >/dev/null 2>&1; then docker --version || true; else echo "missing: docker"; fi
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl is-active forgejo-runner 2>/dev/null || true
|
||||
fi
|
||||
if [ -f /var/lib/forgejo-runner/.runner ]; then echo "ok: /var/lib/forgejo-runner/.runner present"; fi
|
||||
'
|
||||
elif [ -n "${RUNNER_HOST}" ]; then
|
||||
echo "ssh missing; skipping legacy host probe"
|
||||
else
|
||||
echo "RUNNER_HOST unset; skipping legacy host probe"
|
||||
fi
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
id: FORGE-WP-0001
|
||||
type: workplan
|
||||
title: "railiance-forge repository foundation"
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: finished
|
||||
owner: codex
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
id: FORGE-WP-0002
|
||||
type: workplan
|
||||
title: "Canonical registry docs and read-only forge operations"
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: finished
|
||||
owner: codex
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
id: FORGE-WP-0003
|
||||
type: workplan
|
||||
title: "Gitea Actions runner substrate for Railiance workloads"
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: finished
|
||||
owner: codex
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
id: FORGE-WP-0004
|
||||
type: workplan
|
||||
title: "Standard public Gitea HTTPS root endpoint"
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: finished
|
||||
owner: codex
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
id: FORGE-WP-0005
|
||||
type: workplan
|
||||
title: "Remove public Gitea NodePort side door"
|
||||
domain: railiance
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: finished
|
||||
owner: codex
|
||||
Loading…
Add table
Add a link
Reference in a new issue