@remits/remits-cli 0.1.120 → 0.1.122
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.
- package/index.js +137 -16
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +6 -0
- package/skills/remits-cli/references/branch-variants.md +8 -1
- package/skills/remits-cli/references/command-reference.md +1 -1
- package/skills/remits-cli/references/component-integrity.md +27 -7
- package/skills/remits-cli/references/development-loop.md +24 -2
package/index.js
CHANGED
|
@@ -19,6 +19,9 @@ const openBrowser = (openModule && typeof openModule === 'function')
|
|
|
19
19
|
|
|
20
20
|
//const DEFAULT_BASE_URL = process.env.REMITS_BASE_URL || 'http://localhost:8080';
|
|
21
21
|
const DEFAULT_BASE_URL = process.env.REMITS_BASE_URL || 'https://remits-529558023549.us-east5.run.app';
|
|
22
|
+
|
|
23
|
+
// How many compile failures print their full message before the rest are listed by identifier only.
|
|
24
|
+
const COMPILE_FAILURES_SHOWN = 5;
|
|
22
25
|
const SESSION_DIR = path.join(os.homedir(), '.remits-cli');
|
|
23
26
|
const SESSIONS_FILE = path.join(SESSION_DIR, 'sessions.json');
|
|
24
27
|
const CONFIG_FILE = path.join(SESSION_DIR, 'config.json');
|
|
@@ -2817,7 +2820,7 @@ async function pushComponentsCommand(flags) {
|
|
|
2817
2820
|
// or, worse, conclude staging was broken and go on reading trunk. Scaled by payload size, floored at
|
|
2818
2821
|
// the old default, and overridable.
|
|
2819
2822
|
const api = buildAxios(baseUrl, session.token, stageTimeoutMs(components, flags));
|
|
2820
|
-
const
|
|
2823
|
+
const stagePayload = {
|
|
2821
2824
|
token: session.token,
|
|
2822
2825
|
accountId,
|
|
2823
2826
|
branchName,
|
|
@@ -2846,11 +2849,50 @@ async function pushComponentsCommand(flags) {
|
|
|
2846
2849
|
// Kept for a platform that predates stageMode. Same meaning it always had.
|
|
2847
2850
|
replace: !changedOnly,
|
|
2848
2851
|
components
|
|
2849
|
-
}
|
|
2852
|
+
};
|
|
2853
|
+
|
|
2854
|
+
let response;
|
|
2855
|
+
try {
|
|
2856
|
+
response = await stageOrRefuse(api, cwd, stagePayload);
|
|
2857
|
+
} catch (err) {
|
|
2858
|
+
// A REFUSED stage is evidence too. Recording it only on the success path is how a lane ends up with
|
|
2859
|
+
// an envelope that says nothing happened between two packets, when in fact a stage was attempted and
|
|
2860
|
+
// the platform rejected it. The packet is best-effort: a failing stage must surface the STAGE error,
|
|
2861
|
+
// never an error from writing the record of it.
|
|
2862
|
+
try {
|
|
2863
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
2864
|
+
type: 'stage',
|
|
2865
|
+
success: false,
|
|
2866
|
+
claim: 'Components refused at staging',
|
|
2867
|
+
world: buildCommandWorld(err.responseBody || {}, { accountId, dataMode, branchName, workspace, host: normalizeBaseUrl(baseUrl), sourceLayer: 'staged' }),
|
|
2868
|
+
revision: collectVerificationSource(cwd, flags),
|
|
2869
|
+
assertions: [
|
|
2870
|
+
{ id: 'stage', stageMode },
|
|
2871
|
+
{ id: 'stage_refused', value: err.message }
|
|
2872
|
+
],
|
|
2873
|
+
evidenceCategories: ['stage'],
|
|
2874
|
+
rawRefs: { command: 'components stage' },
|
|
2875
|
+
stage: (err.responseBody || {}).stage,
|
|
2876
|
+
compileValidation: (err.responseBody || {}).compileValidation,
|
|
2877
|
+
changedFromWorkingTree: changedFromWorkingTree || [],
|
|
2878
|
+
unrepresentableChanges: unstageable
|
|
2879
|
+
}, { quiet: true });
|
|
2880
|
+
} catch (packetError) {
|
|
2881
|
+
// Deliberately swallowed and named, not silent.
|
|
2882
|
+
console.error('Could not record the refused-stage verification packet: ' + packetError.message);
|
|
2883
|
+
}
|
|
2884
|
+
throw err;
|
|
2885
|
+
}
|
|
2850
2886
|
response.changedFromWorkingTree = changedFromWorkingTree || [];
|
|
2851
2887
|
response.changedFromWorkingTreeAvailable = changedFromWorkingTree !== null;
|
|
2852
2888
|
response.requestedStageMode = stageMode;
|
|
2853
2889
|
response.unrepresentableChanges = unstageable;
|
|
2890
|
+
if (response.success === false) {
|
|
2891
|
+
// A 200 that says `success:false`. The 422 path throws out of stageOrRefuse above and never lands
|
|
2892
|
+
// here, so this stays as the belt-and-braces case — print whatever detail came with it first.
|
|
2893
|
+
printCompileValidation(response.compileValidation, { stderr: true });
|
|
2894
|
+
throw new Error(response.message || 'Server stage failed');
|
|
2895
|
+
}
|
|
2854
2896
|
|
|
2855
2897
|
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
2856
2898
|
type: 'stage',
|
|
@@ -2947,6 +2989,17 @@ async function stageOrRefuse(api, cwd, payload) {
|
|
|
2947
2989
|
refusal.refused = true;
|
|
2948
2990
|
throw refusal;
|
|
2949
2991
|
}
|
|
2992
|
+
if (body && body.message) {
|
|
2993
|
+
// The server's `message` is ONE line derived from the FIRST failure. The rest of the detail is in
|
|
2994
|
+
// the body, and the caller cannot see the body — an agent handed "compile failed for Reader 41"
|
|
2995
|
+
// over a stage with nine broken components fixes one, re-stages, and pays nine round trips. Render
|
|
2996
|
+
// the whole list HERE, where the body is still in hand, then throw the one-line summary.
|
|
2997
|
+
printCompileValidation(body.compileValidation, { stderr: true });
|
|
2998
|
+
const failure = new Error(body.message);
|
|
2999
|
+
failure.responseBody = body;
|
|
3000
|
+
failure.status = status;
|
|
3001
|
+
throw failure;
|
|
3002
|
+
}
|
|
2950
3003
|
throw err;
|
|
2951
3004
|
}
|
|
2952
3005
|
}
|
|
@@ -2967,6 +3020,60 @@ function printComponentPolicy(response) {
|
|
|
2967
3020
|
(policy.overrides || []).forEach((id) => console.log('Policy override recorded for rule: ' + id));
|
|
2968
3021
|
}
|
|
2969
3022
|
|
|
3023
|
+
/**
|
|
3024
|
+
* What the platform compiled, and what it could not.
|
|
3025
|
+
*
|
|
3026
|
+
* Prints on BOTH outcomes, including `attempted: 0`. A silent pass and a pass that validated nothing look
|
|
3027
|
+
* identical otherwise, and they are not the same claim: when git cannot identify the changed set the
|
|
3028
|
+
* server has no workset to validate and says so (`skipped[].reason`). Reporting that as silence would be
|
|
3029
|
+
* the same confident-wrong-number this surface exists to remove.
|
|
3030
|
+
*/
|
|
3031
|
+
function printCompileValidation(validation, options) {
|
|
3032
|
+
// Failures go to stderr so `--json` stdout stays parseable: a caller that asked for JSON is a program,
|
|
3033
|
+
// and a refusal must not put prose in the middle of its document.
|
|
3034
|
+
const emit = (options && options.stderr) ? console.error : console.log;
|
|
3035
|
+
if (!validation || validation.attempted == null) return;
|
|
3036
|
+
const failures = Array.isArray(validation.failures) ? validation.failures : [];
|
|
3037
|
+
if (validation.attempted === 0) {
|
|
3038
|
+
const reasons = (Array.isArray(validation.skipped) ? validation.skipped : [])
|
|
3039
|
+
.map((entry) => (entry && entry.reason) || '')
|
|
3040
|
+
.filter((reason) => reason && reason !== 'no-compilable-source');
|
|
3041
|
+
if (reasons.length) {
|
|
3042
|
+
emit('Compile validation: NOT RUN — ' + reasons.join(', ') +
|
|
3043
|
+
'. Nothing in this stage was compile-checked.');
|
|
3044
|
+
if (reasons.includes('workset-unknown')) {
|
|
3045
|
+
emit(' git could not identify the changed set, so there was no workset to validate.');
|
|
3046
|
+
emit(' Stage from a git working tree, or re-stage the component explicitly, to get the check.');
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
return;
|
|
3050
|
+
}
|
|
3051
|
+
const status = validation.success === true ? 'passed' : 'failed';
|
|
3052
|
+
emit('Compile validation: ' + status + ' — ' + validation.passed + '/' + validation.attempted +
|
|
3053
|
+
' compiled in ' + validation.durationMs + 'ms' +
|
|
3054
|
+
(validation.concurrency ? ' (' + validation.concurrency + ' parallel)' : ''));
|
|
3055
|
+
const shown = failures.slice(0, COMPILE_FAILURES_SHOWN);
|
|
3056
|
+
shown.forEach((failure) => {
|
|
3057
|
+
emit(' ' + (failure.type || 'component') + ' ' + (failure.id || failure.name || '') +
|
|
3058
|
+
': ' + (failure.message || 'compile failed'));
|
|
3059
|
+
});
|
|
3060
|
+
if (failures.length > shown.length) {
|
|
3061
|
+
// Named, not counted. "3 more failure(s)" sends an agent back for another round trip to learn WHICH
|
|
3062
|
+
// three; the identifiers are already in hand and cost one line.
|
|
3063
|
+
emit(' ' + (failures.length - shown.length) + ' more failed: ' +
|
|
3064
|
+
failures.slice(COMPILE_FAILURES_SHOWN)
|
|
3065
|
+
.map((failure) => (failure.type || 'component') + ' ' + (failure.id || failure.name || '?'))
|
|
3066
|
+
.join(', '));
|
|
3067
|
+
emit(' Re-run with --json for every compile message.');
|
|
3068
|
+
}
|
|
3069
|
+
if (validation.success !== true) {
|
|
3070
|
+
// The lane is written BEFORE it is validated, because the compile has to resolve through the staged
|
|
3071
|
+
// overlay to see the source it is judging. So a refusal does not mean "nothing was staged".
|
|
3072
|
+
emit(' The rejected source IS in the lane: staging writes, then compiles what it wrote.');
|
|
3073
|
+
emit(' A run in this lane will resolve the broken component until you fix it and re-stage.');
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
|
|
2970
3077
|
function shouldPrintFullComponentResponse(flags) {
|
|
2971
3078
|
return flagEnabled(flags.verbose);
|
|
2972
3079
|
}
|
|
@@ -3088,6 +3195,7 @@ function printStageSummary(response, flags) {
|
|
|
3088
3195
|
if (overlay != null) {
|
|
3089
3196
|
console.log('Materialized overlay now held by the lane:', overlay, 'component(s)');
|
|
3090
3197
|
}
|
|
3198
|
+
printCompileValidation(response.compileValidation);
|
|
3091
3199
|
|
|
3092
3200
|
// The trap `--changed-only` leaves behind: it merges, so a lane inherited from an earlier full snapshot
|
|
3093
3201
|
// keeps every one of those entries resolving ahead of committed source. Silence here is what made
|
|
@@ -4501,7 +4609,22 @@ async function commitComponentsCommand(flags) {
|
|
|
4501
4609
|
const landing = await acquireLandingLease(flags, accountId, branchName);
|
|
4502
4610
|
try {
|
|
4503
4611
|
if (!skipGit) {
|
|
4504
|
-
console.log('Phase 1/
|
|
4612
|
+
console.log('Phase 1/4: staged compile validation');
|
|
4613
|
+
// MERGE semantics on purpose (`--changed-only`, never `--workset`). A workset stage RECONCILES the
|
|
4614
|
+
// lane — it deletes every entry outside the git changed set — and the lane is frequently shared. A
|
|
4615
|
+
// validation pass must not be able to delete another agent's staged work as a side effect of somebody
|
|
4616
|
+
// running `components commit`. This adds the changed components to the lane and leaves the rest alone.
|
|
4617
|
+
await pushComponentsCommand(Object.assign({}, flags, {
|
|
4618
|
+
branch: branchName,
|
|
4619
|
+
'account-id': accountId,
|
|
4620
|
+
mode: 'stage',
|
|
4621
|
+
workset: false,
|
|
4622
|
+
'replace-lane': false,
|
|
4623
|
+
replaceLane: false,
|
|
4624
|
+
'changed-only': true
|
|
4625
|
+
}));
|
|
4626
|
+
|
|
4627
|
+
console.log('Phase 2/4: local git commit/push');
|
|
4505
4628
|
const status = runGit(cwd, 'git status --porcelain');
|
|
4506
4629
|
if (status || allowEmpty) {
|
|
4507
4630
|
runGit(cwd, 'git add -A');
|
|
@@ -4528,14 +4651,12 @@ async function commitComponentsCommand(flags) {
|
|
|
4528
4651
|
console.log('Skipping local git phase. Prefer `remits-cli components sync` if you only need the server sync step.');
|
|
4529
4652
|
}
|
|
4530
4653
|
|
|
4531
|
-
console.log('Phase
|
|
4654
|
+
console.log('Phase 3/4: server sync');
|
|
4532
4655
|
if (flagEnabled(flags.safe)) {
|
|
4533
|
-
//
|
|
4534
|
-
//
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
console.log(' --safe: the branch is already pushed. The gate below stops the platform from WRITING a');
|
|
4538
|
-
console.log(' surprising plan; it cannot un-push. Use `components sync --safe` alone for a pre-push gate.');
|
|
4656
|
+
// Compile validation already ran before the push. The sync safety gates still run after the push
|
|
4657
|
+
// because they need the server's remote-branch plan.
|
|
4658
|
+
console.log(' --safe: compile validation ran before git push; the sync plan gate below runs before');
|
|
4659
|
+
console.log(' the platform writes trunk rows or ComponentVariant overlays.');
|
|
4539
4660
|
}
|
|
4540
4661
|
const syncResponse = await syncComponentsCommand({
|
|
4541
4662
|
...flags,
|
|
@@ -4544,7 +4665,7 @@ async function commitComponentsCommand(flags) {
|
|
|
4544
4665
|
});
|
|
4545
4666
|
|
|
4546
4667
|
if (!skipGit) {
|
|
4547
|
-
console.log('Phase
|
|
4668
|
+
console.log('Phase 4/4: local fast-forward pull');
|
|
4548
4669
|
runGit(cwd, 'git fetch origin ' + shellQuote(branchName));
|
|
4549
4670
|
const expectedSha = syncResponse && syncResponse.sync && syncResponse.sync.postSyncSha;
|
|
4550
4671
|
if (expectedSha) {
|
|
@@ -11144,13 +11265,13 @@ function printComponentsHelp(subcommand) {
|
|
|
11144
11265
|
if (subcommand === 'commit') {
|
|
11145
11266
|
console.log('Usage: remits-cli components commit [--safe] [--message "msg"] [--allow-empty true|false] [--skip-git true|false] [--branch BRANCH] [--data-mode test|prod] [--force-tombstones]');
|
|
11146
11267
|
console.log('');
|
|
11147
|
-
console.log('Runs local git add/commit/push,
|
|
11268
|
+
console.log('Runs staged compile validation, local git add/commit/push, server sync, then fast-forward pull.');
|
|
11269
|
+
console.log('Prefer explicit stage + git + components sync when you need inspectable phases.');
|
|
11148
11270
|
console.log('components commit does not support --dry-run.');
|
|
11149
11271
|
console.log('');
|
|
11150
|
-
console.log('
|
|
11151
|
-
console.log('
|
|
11152
|
-
console.log('
|
|
11153
|
-
console.log('`remits-cli components sync --safe`.');
|
|
11272
|
+
console.log('Phase 1 merge-stages the git-changed components and compile-validates their runtime source.');
|
|
11273
|
+
console.log('It uses --changed-only semantics, so it never reconciles a lane you share with another agent.');
|
|
11274
|
+
console.log('--safe also gates the server sync plan before any trunk rows or ComponentVariant overlays are written.');
|
|
11154
11275
|
return;
|
|
11155
11276
|
}
|
|
11156
11277
|
console.log('Usage: remits-cli components <stage|status|lanes|entries|clear|sync|commit|promotion|branches|branch>');
|
package/package.json
CHANGED
|
@@ -60,6 +60,12 @@ reference named after it.
|
|
|
60
60
|
- **edit → stage → run, every time.** The platform executes whatever is in the staging cache at the
|
|
61
61
|
moment a run starts. Edit a file, run a test without `remits-cli components stage`, and the test runs
|
|
62
62
|
the OLD code. This is the single most common mistake. (`development-loop.md`)
|
|
63
|
+
- **Staging and sync fail early on broken runtime-compiled Groovy.** Stage validates changed source in the
|
|
64
|
+
active lane; sync validates changed/new compiled components before writing DB rows or branch variants.
|
|
65
|
+
Agent files are Utility components internally. A stage refused BY COMPILE VALIDATION still wrote the
|
|
66
|
+
lane — the compile has to resolve through the staged overlay to see the source it is judging — so fix and
|
|
67
|
+
re-stage before running anything in that lane. (A policy or edit-lease refusal is the opposite: it
|
|
68
|
+
returns before any write.) (`development-loop.md`, `component-integrity.md`, `branch-variants.md`)
|
|
63
69
|
- **Stage your WORKSET, not the whole repo: `remits-cli components stage --workset`.** It uploads only
|
|
64
70
|
the components git reports changed and makes the lane hold exactly them. A plain `components stage` is
|
|
65
71
|
a FULL SNAPSHOT — it puts every component in the repo into the lane, so "115 staged" tells a human
|
|
@@ -222,6 +222,12 @@ remits-cli components sync --dry-run # inspect overrides/additions/tombstones wi
|
|
|
222
222
|
remits-cli components sync # writes ComponentVariant overlays ONLY
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
`components stage` validates changed runtime-compiled source through the active branch/workspace lane,
|
|
226
|
+
and `components sync` validates changed/new Groovy source before writing a `ComponentVariant`. This is
|
|
227
|
+
intentional: a branch overlay that cannot compile should fail at staging/sync time, not later when the
|
|
228
|
+
subscriber's workflow first resolves it. `Agent` files in `components/agents/` are the `Utility`
|
|
229
|
+
component kind internally; the CLI and validator normalize both names to the same target.
|
|
230
|
+
|
|
225
231
|
Promotion back to trunk is a **git** operation followed by a **trunk** sync — the platform merges nothing
|
|
226
232
|
for you, and merging a branch promotes its **deletions** as hard deletes. Do not improvise it: follow
|
|
227
233
|
`features/subscriber-branch-promotions.md`. For a real trunk promotion with many `new_` files, use
|
|
@@ -330,7 +336,8 @@ The analogous hazard is different, and you must still respect it:
|
|
|
330
336
|
`removed`, `unchanged`, `skipped`, and `errors` without writing rows, caching the sync SHA, or clearing
|
|
331
337
|
staging. Existing overlay ids appear as `variantId`; an `unchanged` row with `pruned:true` means the
|
|
332
338
|
branch has converged back to trunk and the overlay would be removed. It is rejected on trunk, and
|
|
333
|
-
`components commit --dry-run` is unsupported because `commit` performs
|
|
339
|
+
`components commit --dry-run` is unsupported because `commit` performs compile validation and local git
|
|
340
|
+
writes before syncing.
|
|
334
341
|
- **The sync refuses a wholesale removal.** Above roughly a third of a kind — or **100% of a kind at any
|
|
335
342
|
size** — it aborts that kind, reports why, and points at a rebase. Rebasing is almost always the real
|
|
336
343
|
fix. Only when the removals are genuinely deliberate, re-run with `--force-tombstones`.
|
|
@@ -198,7 +198,7 @@ remits-cli components lanes [--json] # every indexed sta
|
|
|
198
198
|
remits-cli components entries --lane-id <id> [--json|--verbose] # authoritative staged files for one lane
|
|
199
199
|
remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>] [--all] [--json|--verbose] # id alone scopes to one component when unambiguous (ids are type-local; add --component-type if the same id is staged in multiple families); no filter clears the whole branch scope; --all forces the full wipe
|
|
200
200
|
remits-cli components sync [--safe [--yes]] [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--summary] [--changed-only [--changed-since <ref>]] # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
|
|
201
|
-
remits-cli components commit [--safe] [--message "msg"] [--data-mode test|prod] [--force-tombstones] #
|
|
201
|
+
remits-cli components commit [--safe] [--message "msg"] [--data-mode test|prod] [--force-tombstones] # phase 1 merge-stages + compile-validates changed source (never reconciles the lane); --safe gates sync writes
|
|
202
202
|
remits-cli components branches [--json] # branches carrying committed variants, with counts + drift
|
|
203
203
|
remits-cli components branch <name> [--json] # one branch: overridden / added / removed, drift flags, subscribers
|
|
204
204
|
remits-cli components branch <name> --diff <componentId> --component-type <kind> [--json]
|
|
@@ -87,21 +87,41 @@ every live component present at its real id, and nothing extra.
|
|
|
87
87
|
|
|
88
88
|
| Command | What it touches | Danger |
|
|
89
89
|
|---|---|---|
|
|
90
|
-
| `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. | none |
|
|
90
|
+
| `remits-cli components stage` (alias: deprecated `push`) | **Redis staging cache only.** Never mutates the DB or git. The safe iteration surface. A plain stage or `--workset` RECONCILES the lane (entries outside the manifest are dropped); `--changed-only` merges. | none for the DB; a lane you share with another agent is reconciled by the first two |
|
|
91
91
|
| `remits-cli components status` | Reads this lane's staging scope (account/user/branch/workspace) and branch resolution, and lists every other lane on the branch. | none |
|
|
92
92
|
| `remits-cli components clear` | Clears THIS lane's staging cache without changing DB or git. Never touches another workspace lane. | none |
|
|
93
93
|
| `remits-cli components sync` **on trunk** | **Server-side git→DB reconcile of the whole account** (create/update/**delete**/rename). Reads the pushed remote; ignores local files. | **high** |
|
|
94
94
|
| `remits-cli components sync` **on a variant branch** | Writes `ComponentVariant` overlays for that branch only. Never touches trunk rows or the account's trunk branch. When the checkout identifies a subscribing account, the branch-local `account-info.json` is refreshed for that subscriber; `--dry-run` reports the plan without writes. | medium (a missing file becomes a **tombstone** that hides the component from subscribers) |
|
|
95
|
-
| `remits-cli components commit` | **One shot:** `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. Inherits the danger of whichever sync mode the branch selects. | **highest on trunk** |
|
|
95
|
+
| `remits-cli components commit` | **One shot:** changed-source compile validation (a `--changed-only` MERGE stage, so it never reconciles the lane) + `git add -A` + commit + `git push` + **`components sync`** + `git pull --ff-only`. Blindly stages the *entire* working tree (including any drift) and reconciles it into prod. Inherits the danger of whichever sync mode the branch selects. | **highest on trunk** |
|
|
96
96
|
|
|
97
97
|
Key implications:
|
|
98
|
-
- **`stage`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
- **`stage` never touches the DB or git** — stage and test as much as you want. It is safe *for your
|
|
99
|
+
components*; it is not inert *for the lane*. A full snapshot and `--workset` both reconcile the lane to
|
|
100
|
+
their manifest, so on a lane shared with another agent they drop that agent's staged entries. Use
|
|
101
|
+
`--changed-only` when you mean "add mine, leave theirs".
|
|
102
|
+
- **A COMPILE-VALIDATION refusal still wrote the lane; a policy refusal does not.** Read the status code,
|
|
103
|
+
because the two refusals leave opposite states behind:
|
|
104
|
+
- **422 (compile validation)** — the entries are already stored. Validation runs after the write because
|
|
105
|
+
the compile has to resolve through the staged overlay to see the source it is judging. The response
|
|
106
|
+
says so with `laneHoldsRejectedSource`. Until you fix the source and re-stage, a run in that lane
|
|
107
|
+
resolves the broken component.
|
|
108
|
+
- **409 (account policy, or another agent's edit lease)** — refused before anything was written. The lane
|
|
109
|
+
is exactly as it was; nothing to undo.
|
|
110
|
+
- **"Compile validation: NOT RUN" is a real answer.** The check keys off the git changed set. If git
|
|
111
|
+
cannot identify one — you are not in a working tree, or git failed — there is no workset to validate and
|
|
112
|
+
the CLI says `workset-unknown` rather than implying a pass.
|
|
113
|
+
- **`components commit` is the most dangerous command**, not a mere convenience wrapper: it first
|
|
114
|
+
merge-stages and compile-validates the changed runtime source, then `git add -A`, commits, pushes, and
|
|
115
|
+
immediately syncs. Never run it while the tree contains drift or unexplained changes. Prefer the explicit,
|
|
116
|
+
observable `components stage → git commit → git push → components sync → git pull` sequence so each
|
|
117
|
+
phase can be inspected.
|
|
103
118
|
- **`components sync` acts on the pushed remote**, so local edits are invisible to it until committed **and
|
|
104
119
|
pushed**, and a drifted **remote** is dangerous even when your local tree looks fine.
|
|
120
|
+
- **Runtime-compiled component source is validated before durable writes.** When a trunk sync or variant
|
|
121
|
+
sync sees changed/new Groovy source for a `Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`,
|
|
122
|
+
`Test`, `Agent`/`Utility`, or `Tool`, the platform compiles it before accepting the DB row or
|
|
123
|
+
`ComponentVariant` overlay. A compile failure lands in `syncResults.errors` and the branch SHA cache is
|
|
124
|
+
not advanced, so fix the source and retry the same sync.
|
|
105
125
|
- After a successful non-dry-run `components sync` / `components commit`, the server clears the full
|
|
106
126
|
staging scope for that lane (account/user/branch/workspace). This is the expected clean state: old Redis aliases should not keep shadowing
|
|
107
127
|
the newly synced DB rows. `components sync --dry-run` intentionally leaves staging untouched.
|
|
@@ -218,6 +218,28 @@ remits-cli components stage --workset # every edit: stage what you changed
|
|
|
218
218
|
|
|
219
219
|
This uploads your local component changes to the platform's staging cache (Redis, 240-minute TTL). It does NOT commit anything. The platform cannot see your local edits until you stage them.
|
|
220
220
|
|
|
221
|
+
For runtime-compiled components (`Reader`, `Action`, `Embeddable`, `HtmlTemplate`, `Rule`, `Test`,
|
|
222
|
+
`Agent`/`Utility`, and `Tool`), staging also validates the Groovy source that is part of the submitted
|
|
223
|
+
workset. Validation resolves through the same branch/workspace staging lane a later run will use and
|
|
224
|
+
compiles candidates in a small bounded pool, so a syntax/compile error is reported by `components stage`
|
|
225
|
+
instead of waiting for the next workflow to trip over it. A full repository stage does not compile every
|
|
226
|
+
component in the repo; it validates only entries known to be in the current workset, plus explicit partial
|
|
227
|
+
source updates.
|
|
228
|
+
|
|
229
|
+
Two things to read correctly when it refuses:
|
|
230
|
+
|
|
231
|
+
- **The lane already holds what it rejected — on a 422.** The compile has to resolve through the staged
|
|
232
|
+
overlay to see the source it is judging, so the entries are written first and compiled second. `422`
|
|
233
|
+
means "staged, and rejected", not "nothing happened" — fix the source and re-stage before running
|
|
234
|
+
anything in that lane. A `409` is the other kind of refusal (account policy, or another agent's edit
|
|
235
|
+
lease) and writes nothing at all.
|
|
236
|
+
- **Every failure is printed, not just the first.** The one-line error is the first failure; the full list
|
|
237
|
+
(identified by type and id) follows it. Fix them in one pass rather than one round trip each.
|
|
238
|
+
|
|
239
|
+
If git cannot identify the changed set — you are not in a working tree, or git failed — there is no
|
|
240
|
+
workset to validate, and the CLI prints `Compile validation: NOT RUN — workset-unknown` rather than
|
|
241
|
+
silently implying a pass.
|
|
242
|
+
|
|
221
243
|
##### Stage your workset, not the whole repo
|
|
222
244
|
|
|
223
245
|
There are three stage modes, and the difference decides what a run in your lane resolves and what a
|
|
@@ -321,7 +343,7 @@ If no relevant Test component exists yet, consider creating one. Test components
|
|
|
321
343
|
|
|
322
344
|
New test files use the `new_` prefix (e.g., `new_MyTest.groovy`) and no `id:` in the sidecar — see "Creating a component that does not exist yet" in Step 2. Run them **by name** (`remits-cli test run --test "My Test"`) until a sync assigns an id and renames the file.
|
|
323
345
|
|
|
324
|
-
**How to write the Test itself is not a CLI concern** — what a suite can assert, how mocks behave across HTTP/relay boundaries, driving an embeddable in-process, and the front-stage-only rule all live in `guides/
|
|
346
|
+
**How to write the Test itself is not a CLI concern** — what a suite can assert, how mocks behave across HTTP/relay boundaries, driving an embeddable in-process, and the front-stage-only rule all live in `guides/test-components.md`. Read that before authoring a suite.
|
|
325
347
|
|
|
326
348
|
**Option B — Visual verification with Playwright** (for UI changes or when the user wants to "see it"):
|
|
327
349
|
|
|
@@ -362,7 +384,7 @@ They are not interchangeable. The loader's request carries **no path**, so it re
|
|
|
362
384
|
purely from the embeddable-scoped token key's persisted context. The browser `tokenKey` names the account
|
|
363
385
|
preview URL; `embedTokenKey` names the host-loader credential. The response also echoes `injectionType` /
|
|
364
386
|
`renderMode` / `headMode`, which decide what a host actually receives
|
|
365
|
-
(`guides/
|
|
387
|
+
(`guides/embeddable-components.md`).
|
|
366
388
|
|
|
367
389
|
**This works for a `new_` component that has never been synced.** The embed token key carries the
|
|
368
390
|
component NAME as well as its id, so a staged, id-less Embeddable is loader-addressable — you do not
|