@nac3/forge-cli 1.0.57 → 1.0.59
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/dist/chat/doctrine_digest.d.ts.map +1 -1
- package/dist/chat/doctrine_digest.js +12 -0
- package/dist/chat/doctrine_digest.js.map +1 -1
- package/dist/chat/flow_bpmn.d.ts +57 -38
- package/dist/chat/flow_bpmn.d.ts.map +1 -1
- package/dist/chat/flow_bpmn.js +170 -53
- package/dist/chat/flow_bpmn.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +171 -76
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/server.d.ts.map +1 -1
- package/dist/chat/server.js +23 -9
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/tools/pizarron.d.ts.map +1 -1
- package/dist/chat/tools/pizarron.js +17 -8
- package/dist/chat/tools/pizarron.js.map +1 -1
- package/dist/docs/doctrine/concurrency-scale.md +54 -0
- package/dist/docs/doctrine/pizarron.md +20 -10
- package/dist/semantic_graph/last_change.d.ts +9 -0
- package/dist/semantic_graph/last_change.d.ts.map +1 -0
- package/dist/semantic_graph/last_change.js +93 -0
- package/dist/semantic_graph/last_change.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/doctrine/concurrency-scale.md +54 -0
- package/docs/doctrine/pizarron.md +20 -10
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pizarron.d.ts","sourceRoot":"","sources":["../../../src/chat/tools/pizarron.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGrE,eAAO,MAAM,qBAAqB,EAAE,QAkEnC,CAAC;AAMF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"pizarron.d.ts","sourceRoot":"","sources":["../../../src/chat/tools/pizarron.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGrE,eAAO,MAAM,qBAAqB,EAAE,QAkEnC,CAAC;AAMF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,UAAU,CAAC,CAqErB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decideFlowTab } from '../flow_bpmn.js';
|
|
2
2
|
export const SHOW_IN_PIZARRON_SPEC = {
|
|
3
3
|
name: 'forge_show_in_pizarron',
|
|
4
4
|
description: 'Surface STRUCTURED CONTENT (code, table, markdown doc, '
|
|
@@ -104,14 +104,23 @@ export async function runShowInPizarron(args, _ctx) {
|
|
|
104
104
|
display: 'too long',
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
/* Enforce the KIND SELECTION rule server-side: a `graph`
|
|
108
|
-
*
|
|
109
|
-
*
|
|
107
|
+
/* Enforce the KIND SELECTION rule server-side: a `graph` that is really a
|
|
108
|
+
* flow/proceso. First time -> reject with a nudge so the brain redoes it as
|
|
109
|
+
* native BPMN (B); on reincidence -> convert deterministically (A). Some
|
|
110
|
+
* brains ignore the always-on doctrine and keep drawing flows as webs. */
|
|
110
111
|
{
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
const decision = decideFlowTab({ kind, content, title });
|
|
113
|
+
if (decision.action === 'reject') {
|
|
114
|
+
return {
|
|
115
|
+
tool: SHOW_IN_PIZARRON_SPEC.name,
|
|
116
|
+
is_error: true,
|
|
117
|
+
result: { error: decision.message },
|
|
118
|
+
display: 'flow-as-graph -> nudged to bpmn',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (decision.action === 'convert') {
|
|
122
|
+
kind = decision.kind;
|
|
123
|
+
content = decision.content;
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
/* Generate a stable-ish item id for the panel to use as a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pizarron.js","sourceRoot":"","sources":["../../../src/chat/tools/pizarron.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"pizarron.js","sourceRoot":"","sources":["../../../src/chat/tools/pizarron.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Concurrency & scale doctrine
|
|
2
|
+
|
|
3
|
+
Every application Forge designs MUST be sized to its expected
|
|
4
|
+
concurrency from day one. Scale is a RELEVAMIENTO input, not an
|
|
5
|
+
afterthought bolted on when the app falls over.
|
|
6
|
+
|
|
7
|
+
## 1. Ask it during requirements
|
|
8
|
+
|
|
9
|
+
During the greenfield relevamiento -- and when scoping a corrective /
|
|
10
|
+
evolutive change that touches a load-bearing path -- ASK, before
|
|
11
|
+
choosing an architecture:
|
|
12
|
+
|
|
13
|
+
- Peak SIMULTANEOUS users / requests? Give an order of magnitude:
|
|
14
|
+
tens, hundreds, thousands, 10k+.
|
|
15
|
+
- Read-heavy or write-heavy? Steady or bursty (spikes)?
|
|
16
|
+
- Latency budget: interactive (< ~200 ms) or batch/async?
|
|
17
|
+
- Growth horizon: will it 10x within a year?
|
|
18
|
+
- Consistency needs: strong (banking) or eventual (feeds)?
|
|
19
|
+
|
|
20
|
+
If the user does not know, PROPOSE a reasonable default AND write the
|
|
21
|
+
assumption explicitly in the spec / `<root>/.forge/sumi-context.md`.
|
|
22
|
+
Never silently assume "a handful of users".
|
|
23
|
+
|
|
24
|
+
## 2. Design a multi-layer architecture with no bottleneck at any tier
|
|
25
|
+
|
|
26
|
+
Pick the layers that the target number actually needs, and make each
|
|
27
|
+
one non-blocking:
|
|
28
|
+
|
|
29
|
+
- Client: pagination, debounce, optimistic UI, local cache -- do not
|
|
30
|
+
fetch the world on every keystroke.
|
|
31
|
+
- Edge / CDN: serve static + cacheable responses close to the user.
|
|
32
|
+
- App tier: STATELESS request handlers so nodes scale horizontally.
|
|
33
|
+
No in-process session/user state that pins a user to one node
|
|
34
|
+
(put it in a shared store / signed token).
|
|
35
|
+
- Concurrency control: connection pools sized to the DB; queues +
|
|
36
|
+
backpressure to absorb spikes; idempotency keys for retried writes;
|
|
37
|
+
rate limits / circuit breakers on hot paths.
|
|
38
|
+
- Data tier (the usual bottleneck): indexed queries; read replicas
|
|
39
|
+
for read-heavy loads; a cache (Redis / KV) in front of hot reads;
|
|
40
|
+
async jobs for slow writes; partition / shard ONLY when the numbers
|
|
41
|
+
demand it.
|
|
42
|
+
- Realtime: fan out via a broker (Redis pub/sub, a queue, a hub), not
|
|
43
|
+
an N-app-node polling loop.
|
|
44
|
+
|
|
45
|
+
## 3. Proportionality (do not gold-plate)
|
|
46
|
+
|
|
47
|
+
Match the architecture to the number. A 20-user internal tool gets a
|
|
48
|
+
simple single-tier design -- adding sharding + a message broker there
|
|
49
|
+
is a defect too. A 10k-concurrent public app gets the full multi-layer
|
|
50
|
+
treatment. The rule is "no bottleneck AND no gold-plating", sized to
|
|
51
|
+
the stated concurrency.
|
|
52
|
+
|
|
53
|
+
Record the target concurrency and the tier decisions in the spec so
|
|
54
|
+
they are traceable and can be revisited when the number changes.
|
|
@@ -92,13 +92,23 @@ business flow with kind="graph". The user asks for a FLUJO and
|
|
|
92
92
|
gets a force-directed node web instead of a BPMN diagram.
|
|
93
93
|
|
|
94
94
|
SERVER-SIDE ENFORCEMENT (src/chat/flow_bpmn.ts): because some
|
|
95
|
-
brains ignore this rule anyway, the server catches it.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
brains ignore this rule anyway, the server catches it. A graph is
|
|
96
|
+
treated as a flow when its TITLE reads like one (flujo / flow /
|
|
97
|
+
flowchart / proceso / cursograma / pipeline / etapas / ciclo /
|
|
98
|
+
journey / "grafico de procesos" / BPMN / ...) OR when its STRUCTURE
|
|
99
|
+
is an ordered, acyclic, sparse sequence or a labelled-decision graph
|
|
100
|
+
(so a flow with a neutral title like "Alta de cliente" is caught
|
|
101
|
+
too). Then, in two layers:
|
|
102
|
+
(B) the FIRST time, the create-tab is REJECTED with a nudge telling
|
|
103
|
+
you to re-create it as kind="bpmn" -- native BPMN with proper
|
|
104
|
+
startEvent/endEvent, tasks and gateways. Just do it in the same
|
|
105
|
+
response.
|
|
106
|
+
(A) if you RESEND the same graph instead of obeying, the server
|
|
107
|
+
converts it deterministically (synthetic start/end; a labelled
|
|
108
|
+
2-way branch -> exclusive gateway) so the user NEVER sees an
|
|
109
|
+
ugly flow, whatever you send.
|
|
110
|
+
Relationship / dependency / hierarchy graphs (title has grafo /
|
|
111
|
+
relaciones / dependencias / organigrama / entidad-relacion, or a
|
|
112
|
+
dense / cyclic structure) are left untouched. This is a safety net,
|
|
113
|
+
NOT a licence to keep sending graph -- always classify the kind
|
|
114
|
+
yourself.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** relPath (posix slashes) -> unix seconds of the file's most recent commit.
|
|
2
|
+
* Empty map when git is unavailable / not a repo (caller falls to mtime). */
|
|
3
|
+
export declare function fileLastChangeMap(projectRoot: string): Promise<Map<string, number>>;
|
|
4
|
+
/** Resolve a node's last-change ts: git map by path, else fs mtime, else 0. */
|
|
5
|
+
export declare function nodeLastChange(projectRoot: string, relPath: string, gitMap: Map<string, number>): Promise<number>;
|
|
6
|
+
/** Server-side twin of the panel's graphNodePath: the last path-like
|
|
7
|
+
* parenthetical in a node's purpose/detail ("... (src/foo.py)."). */
|
|
8
|
+
export declare function pathFromPurpose(purpose: string): string;
|
|
9
|
+
//# sourceMappingURL=last_change.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"last_change.d.ts","sourceRoot":"","sources":["../../src/semantic_graph/last_change.ts"],"names":[],"mappings":"AAaA;8EAC8E;AAC9E,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBnF;AAED,+EAA+E;AAC/E,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChE,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;sEACsE;AACtE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA4BvD"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-file "last change" timestamp for the galaxy graph colouring (Pablo,
|
|
3
|
+
* 2026-07-05). One `git log` pass yields the most-recent commit time per
|
|
4
|
+
* tracked file; the filesystem mtime is the fallback for untracked files.
|
|
5
|
+
* The panel normalises these across nodes and colours newest -> blue,
|
|
6
|
+
* oldest -> red (full spectrum).
|
|
7
|
+
*
|
|
8
|
+
* ASCII-only.
|
|
9
|
+
*/
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import { stat } from 'node:fs/promises';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
/** relPath (posix slashes) -> unix seconds of the file's most recent commit.
|
|
14
|
+
* Empty map when git is unavailable / not a repo (caller falls to mtime). */
|
|
15
|
+
export function fileLastChangeMap(projectRoot) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
execFile('git', ['log', '--format=%ct', '--name-only', '--no-renames'], { cwd: projectRoot, maxBuffer: 128 * 1024 * 1024, windowsHide: true }, (err, stdout) => {
|
|
18
|
+
const map = new Map();
|
|
19
|
+
if (err || !stdout) {
|
|
20
|
+
resolve(map);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let ts = 0;
|
|
24
|
+
for (const raw of String(stdout).split('\n')) {
|
|
25
|
+
const line = raw.trim();
|
|
26
|
+
if (!line)
|
|
27
|
+
continue;
|
|
28
|
+
if (/^\d+$/.test(line)) {
|
|
29
|
+
ts = parseInt(line, 10);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const rel = line.replace(/\\/g, '/');
|
|
33
|
+
if (!map.has(rel))
|
|
34
|
+
map.set(rel, ts); // git log is newest-first: first wins
|
|
35
|
+
}
|
|
36
|
+
resolve(map);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** Resolve a node's last-change ts: git map by path, else fs mtime, else 0. */
|
|
41
|
+
export async function nodeLastChange(projectRoot, relPath, gitMap) {
|
|
42
|
+
if (!relPath)
|
|
43
|
+
return 0;
|
|
44
|
+
const rel = relPath.replace(/\\/g, '/');
|
|
45
|
+
const g = gitMap.get(rel);
|
|
46
|
+
if (g)
|
|
47
|
+
return g;
|
|
48
|
+
try {
|
|
49
|
+
const st = await stat(join(projectRoot, rel));
|
|
50
|
+
return Math.floor(st.mtimeMs / 1000);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Server-side twin of the panel's graphNodePath: the last path-like
|
|
57
|
+
* parenthetical in a node's purpose/detail ("... (src/foo.py)."). */
|
|
58
|
+
export function pathFromPurpose(purpose) {
|
|
59
|
+
const d = String(purpose || '');
|
|
60
|
+
const cands = [];
|
|
61
|
+
let i = 0;
|
|
62
|
+
while (i < d.length) {
|
|
63
|
+
const open = d.indexOf('(', i);
|
|
64
|
+
if (open < 0)
|
|
65
|
+
break;
|
|
66
|
+
const close = d.indexOf(')', open + 1);
|
|
67
|
+
if (close < 0)
|
|
68
|
+
break;
|
|
69
|
+
cands.push(d.slice(open + 1, close).trim());
|
|
70
|
+
i = close + 1;
|
|
71
|
+
}
|
|
72
|
+
for (let k = cands.length - 1; k >= 0; k--) {
|
|
73
|
+
let inner = cands[k] || '';
|
|
74
|
+
if (!inner || inner.length > 240)
|
|
75
|
+
continue;
|
|
76
|
+
while (inner.length && '.,;'.indexOf(inner.charAt(inner.length - 1)) >= 0)
|
|
77
|
+
inner = inner.slice(0, -1);
|
|
78
|
+
const hasSep = inner.indexOf('/') >= 0 || inner.indexOf('\\') >= 0;
|
|
79
|
+
let hasExt = false;
|
|
80
|
+
const dot = inner.lastIndexOf('.');
|
|
81
|
+
if (dot > 0 && dot < inner.length - 1) {
|
|
82
|
+
const ext = inner.slice(dot + 1);
|
|
83
|
+
hasExt = ext.length <= 8 && /^[a-z0-9]+$/i.test(ext);
|
|
84
|
+
}
|
|
85
|
+
if (!hasSep && !hasExt)
|
|
86
|
+
continue;
|
|
87
|
+
if (inner.indexOf(' ') >= 0 && !hasSep)
|
|
88
|
+
continue;
|
|
89
|
+
return inner;
|
|
90
|
+
}
|
|
91
|
+
return '';
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=last_change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"last_change.js","sourceRoot":"","sources":["../../src/semantic_graph/last_change.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Yujin Forge CLI version. Kept in sync with package.json by the npm
|
|
3
3
|
* "version" lifecycle (scripts/sync_version.mjs) so the two never drift.
|
|
4
4
|
*/
|
|
5
|
-
export declare const VERSION = "1.0.
|
|
5
|
+
export declare const VERSION = "1.0.59";
|
|
6
6
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Concurrency & scale doctrine
|
|
2
|
+
|
|
3
|
+
Every application Forge designs MUST be sized to its expected
|
|
4
|
+
concurrency from day one. Scale is a RELEVAMIENTO input, not an
|
|
5
|
+
afterthought bolted on when the app falls over.
|
|
6
|
+
|
|
7
|
+
## 1. Ask it during requirements
|
|
8
|
+
|
|
9
|
+
During the greenfield relevamiento -- and when scoping a corrective /
|
|
10
|
+
evolutive change that touches a load-bearing path -- ASK, before
|
|
11
|
+
choosing an architecture:
|
|
12
|
+
|
|
13
|
+
- Peak SIMULTANEOUS users / requests? Give an order of magnitude:
|
|
14
|
+
tens, hundreds, thousands, 10k+.
|
|
15
|
+
- Read-heavy or write-heavy? Steady or bursty (spikes)?
|
|
16
|
+
- Latency budget: interactive (< ~200 ms) or batch/async?
|
|
17
|
+
- Growth horizon: will it 10x within a year?
|
|
18
|
+
- Consistency needs: strong (banking) or eventual (feeds)?
|
|
19
|
+
|
|
20
|
+
If the user does not know, PROPOSE a reasonable default AND write the
|
|
21
|
+
assumption explicitly in the spec / `<root>/.forge/sumi-context.md`.
|
|
22
|
+
Never silently assume "a handful of users".
|
|
23
|
+
|
|
24
|
+
## 2. Design a multi-layer architecture with no bottleneck at any tier
|
|
25
|
+
|
|
26
|
+
Pick the layers that the target number actually needs, and make each
|
|
27
|
+
one non-blocking:
|
|
28
|
+
|
|
29
|
+
- Client: pagination, debounce, optimistic UI, local cache -- do not
|
|
30
|
+
fetch the world on every keystroke.
|
|
31
|
+
- Edge / CDN: serve static + cacheable responses close to the user.
|
|
32
|
+
- App tier: STATELESS request handlers so nodes scale horizontally.
|
|
33
|
+
No in-process session/user state that pins a user to one node
|
|
34
|
+
(put it in a shared store / signed token).
|
|
35
|
+
- Concurrency control: connection pools sized to the DB; queues +
|
|
36
|
+
backpressure to absorb spikes; idempotency keys for retried writes;
|
|
37
|
+
rate limits / circuit breakers on hot paths.
|
|
38
|
+
- Data tier (the usual bottleneck): indexed queries; read replicas
|
|
39
|
+
for read-heavy loads; a cache (Redis / KV) in front of hot reads;
|
|
40
|
+
async jobs for slow writes; partition / shard ONLY when the numbers
|
|
41
|
+
demand it.
|
|
42
|
+
- Realtime: fan out via a broker (Redis pub/sub, a queue, a hub), not
|
|
43
|
+
an N-app-node polling loop.
|
|
44
|
+
|
|
45
|
+
## 3. Proportionality (do not gold-plate)
|
|
46
|
+
|
|
47
|
+
Match the architecture to the number. A 20-user internal tool gets a
|
|
48
|
+
simple single-tier design -- adding sharding + a message broker there
|
|
49
|
+
is a defect too. A 10k-concurrent public app gets the full multi-layer
|
|
50
|
+
treatment. The rule is "no bottleneck AND no gold-plating", sized to
|
|
51
|
+
the stated concurrency.
|
|
52
|
+
|
|
53
|
+
Record the target concurrency and the tier decisions in the spec so
|
|
54
|
+
they are traceable and can be revisited when the number changes.
|
|
@@ -92,13 +92,23 @@ business flow with kind="graph". The user asks for a FLUJO and
|
|
|
92
92
|
gets a force-directed node web instead of a BPMN diagram.
|
|
93
93
|
|
|
94
94
|
SERVER-SIDE ENFORCEMENT (src/chat/flow_bpmn.ts): because some
|
|
95
|
-
brains ignore this rule anyway, the server catches it.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
brains ignore this rule anyway, the server catches it. A graph is
|
|
96
|
+
treated as a flow when its TITLE reads like one (flujo / flow /
|
|
97
|
+
flowchart / proceso / cursograma / pipeline / etapas / ciclo /
|
|
98
|
+
journey / "grafico de procesos" / BPMN / ...) OR when its STRUCTURE
|
|
99
|
+
is an ordered, acyclic, sparse sequence or a labelled-decision graph
|
|
100
|
+
(so a flow with a neutral title like "Alta de cliente" is caught
|
|
101
|
+
too). Then, in two layers:
|
|
102
|
+
(B) the FIRST time, the create-tab is REJECTED with a nudge telling
|
|
103
|
+
you to re-create it as kind="bpmn" -- native BPMN with proper
|
|
104
|
+
startEvent/endEvent, tasks and gateways. Just do it in the same
|
|
105
|
+
response.
|
|
106
|
+
(A) if you RESEND the same graph instead of obeying, the server
|
|
107
|
+
converts it deterministically (synthetic start/end; a labelled
|
|
108
|
+
2-way branch -> exclusive gateway) so the user NEVER sees an
|
|
109
|
+
ugly flow, whatever you send.
|
|
110
|
+
Relationship / dependency / hierarchy graphs (title has grafo /
|
|
111
|
+
relaciones / dependencias / organigrama / entidad-relacion, or a
|
|
112
|
+
dense / cyclic structure) are left untouched. This is a safety net,
|
|
113
|
+
NOT a licence to keep sending graph -- always classify the kind
|
|
114
|
+
yourself.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nac3/forge-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.59",
|
|
4
4
|
"description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Pablo Kuschnirof <pablo@rpaforce.com>",
|