Commit Graph
29 Commits
Author SHA1 Message Date
zhangwenjian 8faa8d2aed feat: check the shutdown budget against every stop deadline
The budget is one number in config/settings.yml. The deadlines that have to
cover it are in four other files, none of which anybody edits while thinking
about shutdown - so raising the budget passes every test, deploys, and has the
cleanup callbacks killed on the next release.

Two checks share one arithmetic and one five-second margin.

shutdown-budget-overruns-grace compares preStop + drain + server + cleanup
against terminationGracePeriodSeconds in the shipped manifest. Those two files
are not merely adjacent examples: scripts/k8s/prerun.sh builds the
settings-admin ConfigMap out of config/settings.yml and the Deployment mounts
it, so the manifest deploys that file.

docker-stop-cuts-shutdown-short covers the three ways this container is
stopped: `docker stop` in the release workflow, the same in the Makefile, and
stop_grace_period on a compose service that runs this repository's own image. A
service running a database is not this process and is left alone. The duration
is parsed rather than scanned for digits - compose accepts 1m30s, and reading
the first number out of it would call ninety seconds one.

All three spellings of the deadline are read: --timeout, the deprecated --time,
and the short -t. A deadline the check cannot read is reported as no deadline at
all, so recognising only one of them would call a correct command broken and
send whoever fixed it towards the spelling docker is retiring. The message
quotes the flag back in the spelling it was written in, for the same reason:
suggesting a flag the line does not use is how a tool teaches people to
disbelieve it.

What neither covers is `docker rm -f`, which has no deadline to compare
against: it is SIGKILL by definition. That gap is deliberate, and it is why the
previous commit changed the one place that used it on a container that might
still be running.

Both report at two levels. A budget that already overruns is an ERROR; one that
fits with nothing to spare is a WARN, because it works today and failing the
build on a working configuration is how a project teaches people to ignore its
warnings. The two are exclusive: an overrun satisfies the headroom condition as
well, and an ERROR that always drags a duplicate WARN behind it teaches the same
lesson.

preStop is in the sum although the shipped manifest has no hook. That is the
point - a hook added later is spent before the process is told anything, and a
self-check that could not see it would understate the real budget by however
long somebody set it to, which is worse than not checking. A hook whose duration
cannot be read is reported rather than counted as zero.

The fallbacks for fields the settings file leaves out are read from the
constants in the scanned tree, not copied here; if they are renamed the run
stops instead of going quiet with the wrong numbers.

The wording differs by audience on purpose. At run time this is somebody else's
deployment under constraints the process cannot see, so the log states a
minimum. These checks read files this repository owns, where there is standing
to ask for headroom, so they name a target.

The table in AGENTS.md is relisted while it is being touched: the two new
checks, plus datascope-route-unguarded, which has been missing since it was
added. The hard-coded count is gone - it said seven and there were ten, which is
what a written-down count does. AGENTS.md and docs/contract.md both sent readers
to `go run ./tools/checksilent -h` for the list of checks; that prints
command-line flags and has never printed a check, so both now point at
runChecks.

The yaml parser moves from an indirect requirement to a direct one - it was
already in the module graph - and tidy drops four go.sum lines left over from
two older releases of core.
2026-09-06 22:07:08 +08:00
zhangwenjian 0b78bc1e2e docs📝: write down where the life-cycle phases land in this host
core's contract says what the four phases promise. This says which line of
cmd/api/server.go each of them is, which is the part an application author
cannot read out of core.

Three things are recorded because getting them wrong is silent:

  - BeforeRouter is not the before registry. Those callbacks run from
    runStartupHooks, which is after initRouter has built the engine; the phase
    is before it. The two are one line apart in the same function and describing
    them as equivalent is the mistake this paragraph exists to prevent.
  - AfterResource runs again after every configuration reload, so a callback
    there is idempotent with respect to a resource rather than doing nothing
    the second time. The queue consumers are the worked example, in both
    directions: a new adapter must get consumers, the same adapter must not get
    them twice. Identity has to come from where the resource is created -
    GetQueueAdapter and GetQueuePrefix build a fresh wrapper per call, so two
    of them never compare equal however often the adapter underneath changed.
  - Consumers must be registered before the queue is started, and which
    implementation is behind the interface decides whether that matters:
    QueueConfig.Setup hands back queue.NewMemory when there is no redis
    section, and that one does not care; a redis section reaches
    LegacyQueueAdapter, whose Register cannot report a refusal. The table says
    so rather than leaving "only on redis" as a claim. What is silent is the
    registration alone - every publish afterwards is refused with ErrNoHandler
    and logged at error level, so the symptom is missing log rows plus a lot of
    noise, not a quiet nothing.

The third-layer table loses its "queue consumers are lost after a hot reload"
row, which is what AfterResource is for, and gains the honest replacement: the
four phases are the only mount points there are. There is no "after the routes
are installed, before the socket is listening".

AfterListen is described as the port being bound rather than Serve being in its
accept loop. Serve runs on another goroutine and may not have reached the first
Accept; what is true is that the bind returned, so the kernel is queueing
connections. A bind that fails produces no phase at all - the error returns
from run() and the banner never prints.
2026-09-05 22:31:12 +08:00
zhangwenjian f64115e03a docs📝: state what an off-convention migration file name does
The naming rule was documented; what happens when it is broken was not.
It now panics naming the offending file, which is worth saying out loud
because the alternative it replaced was silent: a name that is not a
timestamp used to register as its own version, and that migration would
never run and never report anything.

Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx
2026-09-05 11:14:01 +08:00
zhangwenjian a2524c31bf docs📝(contract): state the two menu seed rules a caller cannot infer
Sort has an upper bound: sys_menu.sort is built as a tinyint, sqlite ignores
the width, and an overflow surfaces as Error 1264 partway through a migration
rather than as a rejected value.

MenuSpec carries no menu name, and the host synthesises one from the app code
and the spec code rather than using Code directly - two applications both
choosing "list" would otherwise share a keep-alive cache key on the frontend.
Nothing in the type says so, and every Seeder implementer would have to
rediscover it.

Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx
2026-09-05 11:14:01 +08:00
zhangwenjian d3e7f46a46 docs📝(contract): point applications at core, not at the host
The list of stable packages named four packages of this repository, on
the stated grounds that deduplicating app/demo's imports produces
exactly those four. That reasoning was wrong in the one direction that
matters: it sends a third-party author to depend on the host, and the
host is a fork that every user edits. `go-admin` is also not a
resolvable module path - it has no dot in its first element - so an
application cannot require it at all without a replace directive, which
is ignored outside the main module.

Rewritten around what core promises instead, and around a different
question: not "which packages does an application import" but "which
conventions fail without saying anything". Those are now spelled out
one by one, each with the mechanism that makes it silent - the response
envelope the frontend reads by `code`, the tenant-scoped connection,
`create_by` and the soft-delete marker, the data-scope middleware, the
transaction shape, and the `apps/` prefix a packaged application's menu
component must carry.

Also states two things the document was missing: installing an
application means trusting it with the host's database connection, at
the same level of trust as importing any other Go package - there is no
sandbox here and this does not pretend otherwise - and wiring an
application in touches two places, not one, where missing the second
means the migrations simply do not run, with no error.

Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx
2026-09-05 11:14:01 +08:00
zhangwenjian 15fb128236 docs📝: describe the rules rather than who follows them
The warning on Authorizator matters to anyone keeping a copy of that file, not
to one particular consumer, and it reads better addressed to all of them: check
what reads those context keys before taking this change.
2026-09-01 19:56:27 +08:00
zhangwenjian ab28fa7bed docs📝: write down what a third-party app may depend on 2026-09-01 17:45:41 +08:00
zhangwenjian 1d551a10ab docs📝: 新增 AGENTS.md 与架构说明
AGENTS.md 是给 AI 编码工具与新贡献者的约定,只记录「不遵守就会出错」的
规则,技术栈版本与命令交由 go.mod 和 Makefile 表达,避免文档与代码脱节。
标准写法指向 app/demo/——那是可编译、有测试的参照物,文档与它冲突时以它
为准。

docs/architecture.md 承载不易从代码直接读出的语义:DataScope 五档的过滤
方式、定时任务的 JobExec 接口、多数据源约束、迁移目录的分工。

内容整理自此前未纳入版本控制的 CLAUDE.md,撰写时逐条对照代码核实,修正
了其中两处失效描述(构建工具已非 Vue CLI;JobExec 的方法是 Exec(interface{})
而非 Run(string))。CLAUDE.md 现改为指向 AGENTS.md 的软链,两者不再各自
漂移。
2026-08-14 16:49:48 +08:00
NipGeihou 73118e49b9 fix: 修复go generate命令不更新Swagger文档问题
修复go generate不更新Swagger文档问题,并更新生成后文档文件
2023-06-20 00:37:59 +08:00
NaturalGao 1f8babd9e7 fix: fix sys_router && add swag commond 2022-08-25 01:19:55 +08:00
NaturalGao 8e8fe906fd perf: update swag 2022-08-25 01:00:57 +08:00
zhangwenjian 3fd34db3c1 docs📝: 修改接口文档 2021-06-29 16:30:47 +08:00
zhangwenjian acdcd0c867 refactor🎨:接口文档重新生成 2021-06-25 11:33:55 +08:00
zhangwenjian 1ab11c46ba docs📝: 更新swagger文档 2021-06-20 00:51:55 +08:00
zhangwenjian e59dd88f97 docs📝: 更新api文档 2021-06-03 09:16:53 +08:00
linwenxiang 97d9590d05 doc :white_check_mark: 修复swagger文档 2021-04-23 14:59:53 +08:00
wenjianzhang 8faf6c9aba docs📝 :更新readme 2021-03-25 19:14:42 +08:00
linwenxiang 12893fc09d 修复swagger无法生成文档 2021-03-24 18:52:59 +08:00
wenjianzhang b9e9d7178b fix:🐛 重新生成文档,并修复之前引用结构体错误问题 2020-11-29 00:27:52 +08:00
zhangwenjian c851e82762 docs:文档更新 2020-10-23 23:34:32 +08:00
zhangwenjian c69a7d9fd9 feat: format reference 2020-08-28 00:02:53 +08:00
zhangwenjian 5a793b9e34 fix :dir insert bug 2020-08-23 22:55:57 +08:00
zhangwenjian b9d0318b45 doc: swagger add auth tag 2020-07-18 14:24:59 +08:00
zhangwenjian 22dc5f3407 doc: upgrade doc 2020-07-15 22:07:23 +08:00
zhangwenjian ba3f4b7c23 docs: update init 2020-06-28 22:47:49 +08:00
zhangwenjian c0b8199c36 Fix problems modifying personal information,Upgrade route configuration format 2020-05-25 21:30:29 +08:00
zhangwenjian 8faff5384f 添加js模板,页面模板,去掉数据库数据初始化,去掉sql文件中没用语句 2020-05-01 14:25:14 +08:00
zhangwenjian 65dbf587f9 Code generation module 2020-03-19 22:04:38 +08:00
zhangwenjian e027e6a259 update project path 2020-03-18 23:09:37 +08:00