🎉 feat: 建立阶段 0 工程骨架与 OpenAPI 契约机器化(WP-01 起步)

- Makefile:统一命令入口(backend/engine/docker/openapi/generate,含 make help)
- backend:FastAPI + uv 最小工程,统一错误体(契约第 7 节)、ADR-015 平台 ID 生成与校验(uuid-utils);pytest 7 项、ruff、mypy strict、启动冒烟全部通过
- engine:灵台引擎 C++23/CMake 骨架,按 Lingtai C ABI 契约实现 lingtai.h(13 个 HjStatus、版本协商、句柄表防二次释放、线程局部错误上下文);doctest 5 用例通过,nm 验证仅导出 hj_* 符号
- openapi:OpenAPI 3.1 单一真源初始版(错误体/分页/Money/首批平台 ID/ContentBlock 全 21 类型/流事件 envelope),Redocly lint 0 错误、bundle 通过
- frontend:Next.js 最小骨架,openapi-typescript 生成 API 类型并通过 strict tsc
- docker:hunjing-postgres(pgvector:17)compose;.env.example 与 config 示例
- ADR-025:登记测试与契约工具链(doctest/libFuzzer/Redocly/openapi-typescript/uuid-utils)
- desktop:Tauri v2 占位与初始化说明(WP-10 启动)
This commit is contained in:
chenxin committed 2026-09-09 21:45:04 +08:00
1 parent 5da1efe921
commit 3fbcd0437c
32 files changed
+11459

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
// 灵台引擎核心(太一):版本协商、句柄表与错误上下文的最小实现。
// 全部入口按不可信输入处理;异常不得跨越 ABI 边界。
#include "hunjing/lingtai.h"
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_set>
// 不透明句柄的实际布局(不进入契约;仅本翻译单元可见字段)。
struct HjEngine {
uint32_t abi_major;
};
struct HjBuffer {
size_t len;
};
namespace {
// 线程局部错误消息(脱敏、UTF-8);下一次引擎调用覆盖。
thread_local std::string g_last_error;
void set_last_error(const char *message) { g_last_error = message; }
// 句柄表:登记活跃引擎句柄,支撑二次释放与非法句柄检测(不崩溃)。
std::mutex g_handle_table_mutex;
std::unordered_set<HjEngine *> g_handle_table;
} // namespace
extern "C" {
uint32_t hj_abi_version(void) { return HJ_ABI_VERSION; }
HjStatus hj_engine_new(uint32_t required_abi_major, HjEngine **out_engine) {
try {
set_last_error("");
if (out_engine == nullptr) {
set_last_error("out_engine 不能为空");
return HJ_ERR_INVALID_ARGUMENT;
}
*out_engine = nullptr;
if (required_abi_major != HJ_ABI_VERSION) {
set_last_error("ABI major 不匹配:拒绝初始化,不静默降级");
return HJ_ERR_UNSUPPORTED_ABI;
}
auto *engine = new (std::nothrow) HjEngine{required_abi_major};
if (engine == nullptr) {
set_last_error("引擎内存分配失败");
return HJ_ERR_OUT_OF_MEMORY;
}
{
const std::lock_guard<std::mutex> lock(g_handle_table_mutex);
g_handle_table.insert(engine);
}
*out_engine = engine;
return HJ_OK;
} catch (...) {
// 入口捕获一切异常并转换为 HjStatus(契约禁令)。
set_last_error("引擎内部错误");
return HJ_ERR_INTERNAL;
}
}
HjStatus hj_engine_free(HjEngine *engine) {
try {
set_last_error("");
{
const std::lock_guard<std::mutex> lock(g_handle_table_mutex);
if (g_handle_table.erase(engine) == 0) {
set_last_error("句柄不在登记表:已释放或非法");
return HJ_ERR_HANDLE_INVALID;
}
}
delete engine;
return HJ_OK;
} catch (...) {
set_last_error("引擎内部错误");
return HJ_ERR_INTERNAL;
}
}
HjStatus hj_buffer_free(HjBuffer *buffer) {
try {
set_last_error("");
if (buffer == nullptr) {
set_last_error("buffer 句柄为空");
return HJ_ERR_INVALID_ARGUMENT;
}
free(buffer);
return HJ_OK;
} catch (...) {
set_last_error("引擎内部错误");
return HJ_ERR_INTERNAL;
}
}
const void *hj_buffer_data(const HjBuffer *buffer) {
if (buffer == nullptr) {
return nullptr;
}
return static_cast<const void *>(buffer + 1);
}
size_t hj_buffer_len(const HjBuffer *buffer) {
if (buffer == nullptr) {
return 0;
}
return buffer->len;
}
const char *hj_last_error(void) { return g_last_error.c_str(); }
} // extern "C"