feat: rename to issue-core and add task ingestion endpoint

Renames the package, distribution, CLI alias, Makefile targets, and
working directory from issue-facade to issue-core, signalling its
role as the authoritative task lifecycle manager for the Coulomb org
(peer to activity-core, rules-core, project-core).

Adds POST /issues/ ingestion endpoint for activity-core's IssueSink,
under a new optional [api] extra. The endpoint is served by `issue
serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or
X-API-Key header), and routes the TaskSpec payload to the configured
default backend with full traceability metadata embedded in
sync_metadata.

- T01: Python package issue_tracker -> issue_core, dir rename
- T02: registered in state hub under custodian domain
- T03: INTENT.md (what it is, what it isn't, how it fits)
- T04: SCOPE.md (in/out-of-scope, integration boundaries)
- T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests
- T06: docs/nats-task-ingestion.md design stub

Closes ISSC-WP-0001.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 05:16:27 +02:00
parent 99ea1fbc45
commit b605d970e3
38 changed files with 1324 additions and 361 deletions

View File

@@ -12,8 +12,8 @@ from pathlib import Path
from click.testing import CliRunner
from unittest.mock import Mock, patch, MagicMock
from issue_tracker.cli.main import cli
from issue_tracker.cli.utils import load_backend_configs, save_backend_configs
from issue_core.cli.main import cli
from issue_core.cli.utils import load_backend_configs, save_backend_configs
class TestCLICommands:
@@ -37,9 +37,9 @@ class TestCLICommands:
assert result.exit_code == 0
# Should show either configured backends or "No backends configured"
@patch('issue_tracker.cli.backend_commands.load_backend_configs')
@patch('issue_tracker.cli.backend_commands.save_backend_configs')
@patch('issue_tracker.cli.backend_commands.test_backend_connection')
@patch('issue_core.cli.backend_commands.load_backend_configs')
@patch('issue_core.cli.backend_commands.save_backend_configs')
@patch('issue_core.cli.backend_commands.test_backend_connection')
def test_backend_add_gitea_with_env_token(self, mock_test_conn, mock_save, mock_load):
"""Test adding Gitea backend with environment token."""
# Mock empty initial config
@@ -63,9 +63,9 @@ class TestCLICommands:
assert saved_config['test-gitea']['type'] == 'gitea'
assert saved_config['test-gitea']['token'] == 'test-token'
@patch('issue_tracker.cli.backend_commands.load_backend_configs')
@patch('issue_tracker.cli.backend_commands.save_backend_configs')
@patch('issue_tracker.cli.backend_commands.test_backend_connection')
@patch('issue_core.cli.backend_commands.load_backend_configs')
@patch('issue_core.cli.backend_commands.save_backend_configs')
@patch('issue_core.cli.backend_commands.test_backend_connection')
def test_backend_add_local(self, mock_test_conn, mock_save, mock_load):
"""Test adding local backend."""
mock_load.return_value = {}
@@ -78,7 +78,7 @@ class TestCLICommands:
assert result.exit_code == 0
assert 'Backend \'test-local\' added successfully' in result.output
@patch('issue_tracker.cli.commands.get_backend')
@patch('issue_core.cli.commands.get_backend')
def test_show_command(self, mock_get_backend):
"""Test issue show command."""
# Mock backend and issue
@@ -104,7 +104,7 @@ class TestCLICommands:
assert 'Test description' in result.output
assert 'State: open' in result.output
@patch('issue_tracker.cli.utils.get_backend')
@patch('issue_core.cli.utils.get_backend')
def test_show_command_issue_not_found(self, mock_get_backend):
"""Test issue show command when issue doesn't exist."""
mock_backend = Mock()
@@ -121,7 +121,7 @@ class TestCLICommands:
result = self.runner.invoke(cli, ['--version'])
assert result.exit_code == 0
@patch('issue_tracker.cli.utils.get_backend')
@patch('issue_core.cli.utils.get_backend')
def test_list_command_basic(self, mock_get_backend):
"""Test basic list command functionality."""
# This test will help us identify the existing bug
@@ -157,7 +157,7 @@ class TestBackendConfiguration:
def test_config_directory_creation(self):
"""Test configuration directory is created properly."""
from issue_tracker.cli.utils import get_config_dir
from issue_core.cli.utils import get_config_dir
config_dir = get_config_dir()
assert config_dir.exists()
@@ -177,7 +177,7 @@ class TestBackendConfiguration:
}
# Test saving
with patch('issue_tracker.cli.utils.get_backend_config_path', return_value=config_file):
with patch('issue_core.cli.utils.get_backend_config_path', return_value=config_file):
save_backend_configs(test_config)
# Test loading
@@ -190,7 +190,7 @@ class TestBackendConfiguration:
with tempfile.TemporaryDirectory() as temp_dir:
non_existent_file = Path(temp_dir) / 'nonexistent.json'
with patch('issue_tracker.cli.utils.get_backend_config_path', return_value=non_existent_file):
with patch('issue_core.cli.utils.get_backend_config_path', return_value=non_existent_file):
config = load_backend_configs()
assert config == {}
@@ -204,13 +204,13 @@ class TestEnvironmentTokenDetection:
"""Test GITEA_API_TOKEN environment variable detection."""
mock_getenv.return_value = 'test-env-token'
from issue_tracker.cli.backend_commands import add_backend
from issue_core.cli.backend_commands import add_backend
runner = CliRunner()
with patch('issue_tracker.cli.backend_commands.load_backend_configs', return_value={}):
with patch('issue_tracker.cli.backend_commands.save_backend_configs'):
with patch('issue_tracker.cli.backend_commands.test_backend_connection', return_value=True):
with patch('issue_core.cli.backend_commands.load_backend_configs', return_value={}):
with patch('issue_core.cli.backend_commands.save_backend_configs'):
with patch('issue_core.cli.backend_commands.test_backend_connection', return_value=True):
result = runner.invoke(add_backend, [
'test-gitea', 'gitea'
], input='https://git.example.com\ntestorg\ntestrepo\n')