yjcli 0.1.3__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yjcli-0.1.3 → yjcli-0.2.0}/PKG-INFO +7 -4
- {yjcli-0.1.3 → yjcli-0.2.0}/README.md +6 -3
- {yjcli-0.1.3 → yjcli-0.2.0}/pyproject.toml +1 -1
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/commands/sync.py +37 -2
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-arch-core/SKILL.md +3 -1
- yjcli-0.2.0/src/yjcli/data/skills/yj-scheduler/SKILL.md +124 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/AGENTS.md +1 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/modules/constants.py +2 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/services/scaffold.py +1 -1
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/services/sync.py +2 -14
- {yjcli-0.1.3 → yjcli-0.2.0}/.gitignore +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/LICENSE +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/__init__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/__main__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/cli.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/commands/__init__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/commands/init_cmd.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/commands/platform.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/commands/service.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/__init__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-backend-msa/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-backend-service/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-browser-extension/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-cli/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-frontend/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-mobile-app/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/skills/yj-pc-app/SKILL.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/.gitignore +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/Makefile +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/TOOLS.md +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/make.bat +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/app/.env.development +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/app/.env.examples +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/app/.env.local-dev +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/app/.env.production +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/listen/.env.development +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/listen/.env.examples +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/listen/.env.local-dev +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/listen/.env.production +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/worker/.env.development +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/worker/.env.examples +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/worker/.env.local-dev +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/envs/worker/.env.production +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/frontend/package.json +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/frontend/vite.config.ts +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/scripts/run.bat +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/platform/scripts/run.sh +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/data/templates/settings.json +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/modules/__init__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/modules/fsutil.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/modules/paths.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/modules/prompt.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/services/__init__.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/services/status.py +0 -0
- {yjcli-0.1.3 → yjcli-0.2.0}/src/yjcli/services/wiring.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yjcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Scaffold platforms, services, and AI agent wiring (Cursor/Claude/Codex) for YJ architecture repos.
|
|
5
5
|
Project-URL: Homepage, https://github.com/yseiren87/yjcli
|
|
6
6
|
Project-URL: Repository, https://github.com/yseiren87/yjcli
|
|
@@ -68,7 +68,7 @@ make backend NAME=api # start one service
|
|
|
68
68
|
|
|
69
69
|
Use with `-t` / `--type` (repeatable on `init` / `platform add`):
|
|
70
70
|
|
|
71
|
-
`backend` · `backend-service` · `frontend` · `mobile-app` · `pc-app` · `cli` · `browser-extension`
|
|
71
|
+
`backend` · `backend-service` · `frontend` · `mobile-app` · `pc-app` · `cli` · `browser-extension` · `scheduler`
|
|
72
72
|
|
|
73
73
|
## What `init` creates
|
|
74
74
|
|
|
@@ -129,10 +129,11 @@ Edit `AGENTS.md` only; do not edit `CLAUDE.md` by hand. Use `migrate` when upgra
|
|
|
129
129
|
|
|
130
130
|
- `platform add` only creates platform roots (no services, no skills/Makefile — use `service add` / `sync`).
|
|
131
131
|
- `service add` needs the platform root first (`init` or `platform add`).
|
|
132
|
-
- Non-interactive: pass `-t` / `--all` / `-p` / `-n` as needed
|
|
132
|
+
- Non-interactive: pass `-t` / `--all` / `-p` / `-n` as needed, and pass
|
|
133
|
+
`--yes` for every `sync` command.
|
|
133
134
|
- `doctor` checks the **installed package** assets, not your target repo.
|
|
134
135
|
- `sync skills` / `sync all` remove legacy `.cursor/rules` and `.claude/rules` if present.
|
|
135
|
-
- `sync migrate` also overwrites `AGENTS.md` from the package (destructive)
|
|
136
|
+
- `sync migrate` also overwrites `AGENTS.md` from the package (destructive).
|
|
136
137
|
|
|
137
138
|
## Commands
|
|
138
139
|
|
|
@@ -145,9 +146,11 @@ yjcli init --force
|
|
|
145
146
|
yjcli platform add
|
|
146
147
|
yjcli platform add --all
|
|
147
148
|
yjcli platform add -t cli
|
|
149
|
+
yjcli platform add -t scheduler
|
|
148
150
|
|
|
149
151
|
yjcli service add
|
|
150
152
|
yjcli service add -p backend -n api
|
|
153
|
+
yjcli service add -p scheduler -n operations
|
|
151
154
|
|
|
152
155
|
yjcli sync agents
|
|
153
156
|
yjcli sync skills
|
|
@@ -43,7 +43,7 @@ make backend NAME=api # start one service
|
|
|
43
43
|
|
|
44
44
|
Use with `-t` / `--type` (repeatable on `init` / `platform add`):
|
|
45
45
|
|
|
46
|
-
`backend` · `backend-service` · `frontend` · `mobile-app` · `pc-app` · `cli` · `browser-extension`
|
|
46
|
+
`backend` · `backend-service` · `frontend` · `mobile-app` · `pc-app` · `cli` · `browser-extension` · `scheduler`
|
|
47
47
|
|
|
48
48
|
## What `init` creates
|
|
49
49
|
|
|
@@ -104,10 +104,11 @@ Edit `AGENTS.md` only; do not edit `CLAUDE.md` by hand. Use `migrate` when upgra
|
|
|
104
104
|
|
|
105
105
|
- `platform add` only creates platform roots (no services, no skills/Makefile — use `service add` / `sync`).
|
|
106
106
|
- `service add` needs the platform root first (`init` or `platform add`).
|
|
107
|
-
- Non-interactive: pass `-t` / `--all` / `-p` / `-n` as needed
|
|
107
|
+
- Non-interactive: pass `-t` / `--all` / `-p` / `-n` as needed, and pass
|
|
108
|
+
`--yes` for every `sync` command.
|
|
108
109
|
- `doctor` checks the **installed package** assets, not your target repo.
|
|
109
110
|
- `sync skills` / `sync all` remove legacy `.cursor/rules` and `.claude/rules` if present.
|
|
110
|
-
- `sync migrate` also overwrites `AGENTS.md` from the package (destructive)
|
|
111
|
+
- `sync migrate` also overwrites `AGENTS.md` from the package (destructive).
|
|
111
112
|
|
|
112
113
|
## Commands
|
|
113
114
|
|
|
@@ -120,9 +121,11 @@ yjcli init --force
|
|
|
120
121
|
yjcli platform add
|
|
121
122
|
yjcli platform add --all
|
|
122
123
|
yjcli platform add -t cli
|
|
124
|
+
yjcli platform add -t scheduler
|
|
123
125
|
|
|
124
126
|
yjcli service add
|
|
125
127
|
yjcli service add -p backend -n api
|
|
128
|
+
yjcli service add -p scheduler -n operations
|
|
126
129
|
|
|
127
130
|
yjcli sync agents
|
|
128
131
|
yjcli sync skills
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
+
from yjcli.modules.prompt import abort
|
|
9
10
|
from yjcli.services import sync as sync_svc
|
|
10
11
|
|
|
11
12
|
app = typer.Typer(
|
|
@@ -18,6 +19,26 @@ app = typer.Typer(
|
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def _confirm_sync(*, yes: bool, target: str) -> bool:
|
|
23
|
+
if yes:
|
|
24
|
+
return True
|
|
25
|
+
while True:
|
|
26
|
+
try:
|
|
27
|
+
answer = typer.prompt(
|
|
28
|
+
f"{target} files will be overwritten. Continue? [y/n]",
|
|
29
|
+
default=None,
|
|
30
|
+
show_default=False,
|
|
31
|
+
).strip().lower()
|
|
32
|
+
except (EOFError, typer.Abort):
|
|
33
|
+
abort("non-interactive stdin; pass --yes")
|
|
34
|
+
if answer == "y":
|
|
35
|
+
return True
|
|
36
|
+
if answer == "n":
|
|
37
|
+
typer.echo("sync cancelled.")
|
|
38
|
+
return False
|
|
39
|
+
typer.echo("Enter y or n.", err=True)
|
|
40
|
+
|
|
41
|
+
|
|
21
42
|
@app.command("agents")
|
|
22
43
|
def sync_agents(
|
|
23
44
|
path: Path = typer.Option(
|
|
@@ -28,8 +49,11 @@ def sync_agents(
|
|
|
28
49
|
dir_okay=True,
|
|
29
50
|
resolve_path=True,
|
|
30
51
|
),
|
|
52
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation."),
|
|
31
53
|
) -> None:
|
|
32
54
|
"""Overwrite CLAUDE.md from AGENTS.md (edit AGENTS.md only)."""
|
|
55
|
+
if not _confirm_sync(yes=yes, target="Agent mirror"):
|
|
56
|
+
return
|
|
33
57
|
sync_svc.sync_agents(path)
|
|
34
58
|
|
|
35
59
|
|
|
@@ -43,8 +67,11 @@ def sync_skills(
|
|
|
43
67
|
dir_okay=True,
|
|
44
68
|
resolve_path=True,
|
|
45
69
|
),
|
|
70
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation."),
|
|
46
71
|
) -> None:
|
|
47
72
|
"""Overwrite .cursor/.claude/.agents skills from the yjcli package."""
|
|
73
|
+
if not _confirm_sync(yes=yes, target="Skill"):
|
|
74
|
+
return
|
|
48
75
|
sync_svc.sync_skills(path, force=True)
|
|
49
76
|
|
|
50
77
|
|
|
@@ -58,8 +85,11 @@ def sync_make(
|
|
|
58
85
|
dir_okay=True,
|
|
59
86
|
resolve_path=True,
|
|
60
87
|
),
|
|
88
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation."),
|
|
61
89
|
) -> None:
|
|
62
90
|
"""Overwrite root Makefile/make.bat and installed platforms' run.sh/run.bat."""
|
|
91
|
+
if not _confirm_sync(yes=yes, target="Make and run script"):
|
|
92
|
+
return
|
|
63
93
|
sync_svc.sync_make(path, force=True)
|
|
64
94
|
|
|
65
95
|
|
|
@@ -73,11 +103,14 @@ def sync_all(
|
|
|
73
103
|
dir_okay=True,
|
|
74
104
|
resolve_path=True,
|
|
75
105
|
),
|
|
106
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation."),
|
|
76
107
|
) -> None:
|
|
77
108
|
"""Sync AGENTS.md → CLAUDE.md, skills, make files, and platform run scripts.
|
|
78
109
|
|
|
79
110
|
Does not overwrite AGENTS.md from the package (use `sync migrate` for that).
|
|
80
111
|
"""
|
|
112
|
+
if not _confirm_sync(yes=yes, target="Agent mirror, skill, make, and run script"):
|
|
113
|
+
return
|
|
81
114
|
sync_svc.sync_all(path, force=True)
|
|
82
115
|
|
|
83
116
|
|
|
@@ -95,7 +128,7 @@ def sync_migrate(
|
|
|
95
128
|
False,
|
|
96
129
|
"--yes",
|
|
97
130
|
"-y",
|
|
98
|
-
help="Skip confirmation
|
|
131
|
+
help="Skip confirmation.",
|
|
99
132
|
),
|
|
100
133
|
) -> None:
|
|
101
134
|
"""Force-replace agent wiring from the package (destructive upgrade).
|
|
@@ -104,4 +137,6 @@ def sync_migrate(
|
|
|
104
137
|
Makefile/make.bat, platform run scripts, TOOLS.md, .gitignore,
|
|
105
138
|
.claude/settings.json. Removes legacy .cursor/rules, .claude/rules, .agent.
|
|
106
139
|
"""
|
|
107
|
-
|
|
140
|
+
if not _confirm_sync(yes=yes, target="Agent wiring and root template"):
|
|
141
|
+
return
|
|
142
|
+
sync_svc.sync_migrate(path)
|
|
@@ -30,6 +30,7 @@ mobile-app/ # yj-mobile-app
|
|
|
30
30
|
pc-app/ # yj-pc-app
|
|
31
31
|
cli/ # yj-cli
|
|
32
32
|
browser-extension/ # yj-browser-extension
|
|
33
|
+
scheduler/ # yj-scheduler
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
```text
|
|
@@ -64,7 +65,7 @@ Required files at each service root (no plain `.env`):
|
|
|
64
65
|
`.env.local` convention unless a thin loader maps it to this structure.
|
|
65
66
|
- Env field sets come from `templates/platform/envs/<kind>/` via platform mapping (`listen` / `worker` / `app`).
|
|
66
67
|
- `listen`: `backend` (PORT 8080), `frontend` (PORT 5173)
|
|
67
|
-
- `worker`: `backend-service`, `browser-extension/native_
|
|
68
|
+
- `worker`: `backend-service`, `browser-extension/native_*`, `scheduler` (HOST/PORT optional)
|
|
68
69
|
- `app`: `cli`, `mobile-app`, `pc-app`, `browser-extension` (NAME/VERSION only)
|
|
69
70
|
Do not invent fields outside those templates; environment filenames remain
|
|
70
71
|
extensible through the `.env.<environment>` pattern above.
|
|
@@ -93,6 +94,7 @@ Required files at each service root (no plain `.env`):
|
|
|
93
94
|
| `cli/**` | `yj-cli` |
|
|
94
95
|
| `browser-extension/native_*/**` | `yj-backend-service` |
|
|
95
96
|
| `browser-extension/**` (other) | `yj-browser-extension` |
|
|
97
|
+
| `scheduler/**` | `yj-scheduler` |
|
|
96
98
|
|
|
97
99
|
Never load all platform skills. Never apply a platform skill outside its folder.
|
|
98
100
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yj-scheduler
|
|
3
|
+
description: >-
|
|
4
|
+
Long-running scheduler architecture for multiple periodic batch jobs. Use
|
|
5
|
+
only when editing scheduler/**. Language-agnostic: use an established
|
|
6
|
+
scheduling framework from the project's ecosystem without prescribing a
|
|
7
|
+
specific library. Covers job registration, execution isolation, overlap,
|
|
8
|
+
misfires, timezones, retries, locks, observability, and graceful shutdown.
|
|
9
|
+
Do not use for backend/, backend-service/, frontend/, mobile-app/, pc-app/,
|
|
10
|
+
cli/, or browser-extension/.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# yj-scheduler
|
|
14
|
+
|
|
15
|
+
Requires `yj-arch-core`. Scope: **`scheduler/` only**.
|
|
16
|
+
|
|
17
|
+
## Purpose
|
|
18
|
+
|
|
19
|
+
Run multiple recurring batch jobs in one or more long-running scheduler
|
|
20
|
+
processes. Use an established scheduling framework from the selected language
|
|
21
|
+
ecosystem. Choose the concrete framework from the repository's stack and
|
|
22
|
+
existing dependencies.
|
|
23
|
+
|
|
24
|
+
Do not implement cron parsing, durable scheduling, misfire handling, or a
|
|
25
|
+
`while` + `sleep` scheduling loop from scratch.
|
|
26
|
+
|
|
27
|
+
## Shape
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
scheduler/
|
|
31
|
+
scripts/ # platform-level only
|
|
32
|
+
{scheduler_name}/ # one deployable scheduler process
|
|
33
|
+
apps/ # entry: boot, registration, wiring, shutdown
|
|
34
|
+
services/
|
|
35
|
+
{job_name}/ # one batch workflow
|
|
36
|
+
domains/ # optional: owned concepts only
|
|
37
|
+
modules/ # scheduler adapter, db/api clients, lock, clock
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- One scheduler process may register multiple related jobs.
|
|
41
|
+
- Split jobs into separate `{scheduler_name}` deployables only when they need
|
|
42
|
+
independent deployment, scaling, dependencies, permissions, or failure isolation.
|
|
43
|
+
- Do not add per-job `scripts/` or Makefiles. Root `make scheduler` runs all
|
|
44
|
+
scheduler services; `NAME=<scheduler_name>` runs one.
|
|
45
|
+
- This is a long-running worker platform. `HOST` and `PORT` are optional and
|
|
46
|
+
should exist only when the process exposes a real endpoint.
|
|
47
|
+
|
|
48
|
+
## Roles
|
|
49
|
+
|
|
50
|
+
### entry (`apps`)
|
|
51
|
+
|
|
52
|
+
- Create and configure the scheduling framework, register triggers, wire job
|
|
53
|
+
handlers, start the process, and perform graceful shutdown.
|
|
54
|
+
- Keep registration declarative and discoverable in one entry area.
|
|
55
|
+
- Framework callbacks must be thin: add execution context, call one flow, and
|
|
56
|
+
report its result. No query, transformation, or persistence logic in callbacks.
|
|
57
|
+
|
|
58
|
+
### flow (`services/{job_name}`)
|
|
59
|
+
|
|
60
|
+
- Each periodic job is an independently testable workflow.
|
|
61
|
+
- Own job-specific input, orchestration, batching, checkpointing, and result shape.
|
|
62
|
+
- A failure in one job must not terminate or block unrelated jobs.
|
|
63
|
+
- Make operations idempotent whenever a retry or duplicate trigger is possible.
|
|
64
|
+
|
|
65
|
+
### domain / infra
|
|
66
|
+
|
|
67
|
+
- Add `domains/` only for concepts or shared policy owned by this process;
|
|
68
|
+
persistence is not required. Do not create a domain merely because a job exists.
|
|
69
|
+
- Put framework integration, distributed locks, clocks, persistence, logging,
|
|
70
|
+
and external clients in `modules/`.
|
|
71
|
+
- Do not import another platform's source tree; communicate through explicit
|
|
72
|
+
APIs or generated contracts.
|
|
73
|
+
|
|
74
|
+
## Scheduling policy
|
|
75
|
+
|
|
76
|
+
For every job, make these decisions explicit near its registration or config:
|
|
77
|
+
|
|
78
|
+
- stable job identifier and handler
|
|
79
|
+
- periodic or calendar trigger and explicit timezone
|
|
80
|
+
- whether overlapping executions are allowed
|
|
81
|
+
- misfire behavior (skip, coalesce, or catch up)
|
|
82
|
+
- timeout, retry, and backoff behavior
|
|
83
|
+
- concurrency limit and multi-instance ownership/locking strategy
|
|
84
|
+
|
|
85
|
+
Never rely implicitly on the host's local timezone. Do not scatter schedule
|
|
86
|
+
expressions across callbacks. Fixed business schedules may live in typed code or
|
|
87
|
+
project configuration; expose them as environment-specific settings only when
|
|
88
|
+
deployments genuinely need different schedules.
|
|
89
|
+
|
|
90
|
+
When more than one scheduler instance can run, use the framework's persistent
|
|
91
|
+
job store/single-run facility or a distributed lock. A process-local mutex does
|
|
92
|
+
not prevent duplicate execution across instances.
|
|
93
|
+
|
|
94
|
+
## Reliability and observability
|
|
95
|
+
|
|
96
|
+
- Log job id, execution/run id, scheduled time, actual start, completion or
|
|
97
|
+
failure, duration, and retry attempt using structured fields.
|
|
98
|
+
- Define shutdown behavior: stop accepting triggers, then finish or safely
|
|
99
|
+
cancel active jobs within a bounded grace period.
|
|
100
|
+
- Long-running or high-volume jobs should use bounded batches and checkpoints so
|
|
101
|
+
they can resume safely after interruption.
|
|
102
|
+
- Keep retry scope narrow. Do not retry permanent validation or authorization
|
|
103
|
+
failures as though they were transient infrastructure failures.
|
|
104
|
+
- Inject or wrap the clock in flow/domain tests; do not make business rules
|
|
105
|
+
depend directly on wall-clock calls throughout the codebase.
|
|
106
|
+
|
|
107
|
+
## Import direction
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
entry/framework callback -> flow -> domain? -> infra
|
|
111
|
+
entry -> infra
|
|
112
|
+
flow -> infra
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Forbidden: business logic in trigger callbacks; flow importing entry/framework
|
|
116
|
+
bootstrap; domain importing scheduler framework types; jobs calling one another
|
|
117
|
+
for sequencing when a single coordinating flow owns the workflow.
|
|
118
|
+
|
|
119
|
+
## Editing scope
|
|
120
|
+
|
|
121
|
+
- Stay within `scheduler/{scheduler_name}/` unless the task explicitly changes
|
|
122
|
+
shared contracts or platform wiring.
|
|
123
|
+
- Prefer extending an existing scheduler process for related jobs; do not create
|
|
124
|
+
one deployable per cron expression automatically.
|
|
@@ -29,6 +29,7 @@ When creating, modifying, reviewing, or refactoring code:
|
|
|
29
29
|
| `cli/**` | `yj-cli` |
|
|
30
30
|
| `browser-extension/native_*/**` | `yj-backend-service` |
|
|
31
31
|
| `browser-extension/**` (other) | `yj-browser-extension` |
|
|
32
|
+
| `scheduler/**` | `yj-scheduler` |
|
|
32
33
|
|
|
33
34
|
Never load all platform skills. Never apply a platform skill outside its folder.
|
|
34
35
|
|
|
@@ -10,6 +10,7 @@ PLATFORMS: tuple[str, ...] = (
|
|
|
10
10
|
"pc-app",
|
|
11
11
|
"cli",
|
|
12
12
|
"browser-extension",
|
|
13
|
+
"scheduler",
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
RESERVED_SERVICE_NAMES: frozenset[str] = frozenset({"scripts", "proto"})
|
|
@@ -26,4 +27,5 @@ PLATFORM_ENV: dict[str, tuple[str, dict[str, str]]] = {
|
|
|
26
27
|
"mobile-app": ("app", {}),
|
|
27
28
|
"pc-app": ("app", {}),
|
|
28
29
|
"browser-extension": ("app", {}),
|
|
30
|
+
"scheduler": ("worker", {}),
|
|
29
31
|
}
|
|
@@ -122,7 +122,7 @@ def create_service(root: Path, platform: str, name: str) -> None:
|
|
|
122
122
|
_copy_env_templates(platform, name, base)
|
|
123
123
|
_copy_platform_extras(platform, name, base)
|
|
124
124
|
|
|
125
|
-
if platform in {"backend", "backend-service"}:
|
|
125
|
+
if platform in {"backend", "backend-service", "scheduler"}:
|
|
126
126
|
for sub in ("apps", "services", "domains", "modules"):
|
|
127
127
|
(base / sub).mkdir(exist_ok=True)
|
|
128
128
|
elif platform == "frontend":
|
|
@@ -9,7 +9,7 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
from yjcli.modules import paths
|
|
11
11
|
from yjcli.modules.constants import PLATFORMS
|
|
12
|
-
from yjcli.modules.fsutil import copy_file, copy_tree, ensure_real_dir
|
|
12
|
+
from yjcli.modules.fsutil import copy_file, copy_tree, ensure_real_dir
|
|
13
13
|
from yjcli.modules.prompt import abort
|
|
14
14
|
|
|
15
15
|
_SKILL_DEST_RELS = (
|
|
@@ -114,25 +114,13 @@ def sync_all(root: Path, *, force: bool = True) -> None:
|
|
|
114
114
|
sync_make(root, force=force)
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def sync_migrate(root: Path
|
|
117
|
+
def sync_migrate(root: Path) -> None:
|
|
118
118
|
"""Force-replace agent wiring + root templates from the package (destructive).
|
|
119
119
|
|
|
120
120
|
Unlike `sync all`, this overwrites `AGENTS.md` from the package template,
|
|
121
121
|
wipes skill directories (drops stale skills), removes legacy rules / `.agent`,
|
|
122
122
|
and refreshes Claude settings + TOOLS.md / .gitignore / make scripts.
|
|
123
123
|
"""
|
|
124
|
-
if not yes:
|
|
125
|
-
if not is_interactive():
|
|
126
|
-
abort("migrate requires --yes in non-interactive mode")
|
|
127
|
-
answer = typer.prompt(
|
|
128
|
-
"migrate overwrites AGENTS.md, skills, Makefile, TOOLS.md, "
|
|
129
|
-
".gitignore, .claude/settings.json; removes legacy rules/.agent. "
|
|
130
|
-
"Continue? [y/N]",
|
|
131
|
-
default="N",
|
|
132
|
-
)
|
|
133
|
-
if answer.strip().lower() not in {"y", "yes"}:
|
|
134
|
-
abort("migrate cancelled")
|
|
135
|
-
|
|
136
124
|
typer.echo("== migrate: strip legacy ==")
|
|
137
125
|
_remove_legacy_rules_dirs(root)
|
|
138
126
|
_remove_path(root, Path(".agent"), label="legacy")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|