@manhquy/flow-skill 0.1.0-rc.3 → 0.2.0

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.
@@ -0,0 +1,35 @@
1
+ """Monotonic, lexicographically sortable ids for the graph executor (stdlib only).
2
+
3
+ Format: 16 hex chars of a strictly-increasing nanosecond timestamp + 6 hex chars of
4
+ random suffix, fixed width 22. Within one process ids are STRICTLY increasing (the
5
+ timestamp is bumped past the last issued value, so two calls in the same nanosecond
6
+ still order); across processes ordering follows wall clock at nanosecond grain and
7
+ the random suffix keeps ids unique — the same guarantee LangGraph's UUID6 checkpoint
8
+ ids provide, without a dependency. Fixed width makes string sort == numeric sort.
9
+ """
10
+
11
+ import os
12
+ import threading
13
+ import time
14
+
15
+ _lock = threading.Lock()
16
+ _last_ns = 0
17
+
18
+
19
+ def after(prev):
20
+ """An id strictly greater than `prev`. Used when the journal head was minted by a
21
+ process whose clock ran ahead of ours: ordering here is lexicographic, so a plain
22
+ timestamp id would silently sort BELOW the head and the write would be invisible."""
23
+ with _lock:
24
+ ts = int(prev[:16], 16) + 1
25
+ return "%016x%s" % (ts, os.urandom(3).hex())
26
+
27
+
28
+ def new_id():
29
+ global _last_ns
30
+ with _lock:
31
+ ns = time.time_ns()
32
+ if ns <= _last_ns:
33
+ ns = _last_ns + 1
34
+ _last_ns = ns
35
+ return "%016x%s" % (ns, os.urandom(3).hex())
@@ -0,0 +1,48 @@
1
+ """Predicate registry + stage mapping for the graph executor (stdlib only).
2
+
3
+ The registry is EXACTLY {always, review_green, review_red}: predicates read only
4
+ durable artifacts (here, the recorded gate exit in a checkpoint manifest) - never
5
+ LLMs, never the network. Debt-skips are NOT predicates: they are a plan_next
6
+ traversal semantic keyed on each gate_check node's explicit `stage` field.
7
+ """
8
+
9
+ # Keep in sync with flow.sh STAGES (flow.sh:122) - the closed set every planning
10
+ # gate_check node's `stage` field must belong to. 05-contract is never skippable
11
+ # (cmd_skip's primary guard), so the skippable set is STAGES minus the last.
12
+ STAGES = ["00-idea", "01-research", "02-scope", "03-prd", "04-adr", "05-contract"]
13
+ SKIPPABLE_STAGES = STAGES[:-1]
14
+
15
+
16
+ def always(manifest):
17
+ return True
18
+
19
+
20
+ def review_green(manifest):
21
+ gate = (manifest or {}).get("gate") or {}
22
+ return gate.get("exit") == 0
23
+
24
+
25
+ def review_red(manifest):
26
+ gate = (manifest or {}).get("gate") or {}
27
+ return gate.get("exit") not in (None, 0)
28
+
29
+
30
+ REGISTRY = {"always": always, "review_green": review_green, "review_red": review_red}
31
+
32
+
33
+ def evaluate(name, manifest):
34
+ fn = REGISTRY.get(name)
35
+ if fn is None:
36
+ raise ValueError(f"unregistered predicate: {name}")
37
+ return fn(manifest)
38
+
39
+
40
+ def stage_map(topo):
41
+ """node-id -> stage-name for every gate_check node carrying a `stage` field.
42
+ (Card-subgraph gate_check nodes MUST NOT declare `stage` - a stage on
43
+ card-review would make the review gate debt-skippable; lint enforces this.)"""
44
+ out = {}
45
+ for name, spec in topo.get("nodes", {}).items():
46
+ if spec.get("type") == "gate_check" and spec.get("stage"):
47
+ out[name] = spec["stage"]
48
+ return out
@@ -0,0 +1 @@
1
+ 493d4869516f2f7777b969580af145dece90be88b7c5a435edcce81f6446bad9 flow-topology.json
@@ -0,0 +1,9 @@
1
+ # Digests from GitHub Release harness-cli-v0.1.17 (fetched for pin policy)
2
+ # TAG harness-cli-v0.1.17
3
+ # PROTOCOL_FLOOR harness-cli-v0.1.14
4
+ # DO_NOT_USE harness-cli-v0.1.16
5
+ 5d13f0a024d9d69701534a879581c9433bc0069e04d052e5aacd93b91b3b7a43 harness-cli-linux-x64
6
+ 128c42c251fb31b58582342c09c68be6ed3329340fb1d8f179c88590a4539c97 harness-cli-linux-arm64
7
+ 902968f1383d45eb60e202eb98f8ce6c143cff73cb6f2ccc159a743ca5a70819 harness-cli-macos-x64
8
+ 71217dbe03d111775c5016ab751f8bedbff66e7fd6a4d90f5c8d7e0e4a205b47 harness-cli-macos-arm64
9
+ 2d7edff22ae25308c2ba23ab85bb9acb18d87eea28a9ca4efb2014a0f11a51a6 harness-cli-windows-x64.exe
@@ -0,0 +1,67 @@
1
+ -- flow-owned schema - migration 014 (graph-executor band 014+)
2
+ -- Graph executor foundation: execution journal, checkpoints (evidence manifests, never
3
+ -- blobs), step writes, and operator interrupts. 013 stays reserved/absent (upstream
4
+ -- changeset content sha); 014+ is flow-owned per the 2026-07-26 supersession of the
5
+ -- work-graph red line (see GAP-MATRIX-0.1.17.md).
6
+
7
+ CREATE TABLE graph_execution (
8
+ id TEXT PRIMARY KEY,
9
+ project TEXT NOT NULL,
10
+ kind TEXT NOT NULL CHECK(kind IN ('auto_run','planning','card')),
11
+ topology_version INTEGER NOT NULL,
12
+ topology_hash TEXT NOT NULL,
13
+ status TEXT NOT NULL DEFAULT 'running'
14
+ CHECK(status IN ('running','paused','done','failed','abandoned')),
15
+ outcome TEXT,
16
+ story_id TEXT REFERENCES story(id),
17
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
18
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
19
+ );
20
+
21
+ CREATE INDEX idx_graph_execution_status ON graph_execution(status);
22
+
23
+ CREATE TABLE graph_checkpoint (
24
+ execution_id TEXT NOT NULL REFERENCES graph_execution(id) ON DELETE CASCADE,
25
+ ns TEXT NOT NULL DEFAULT '',
26
+ checkpoint_id TEXT NOT NULL,
27
+ parent_checkpoint_id TEXT,
28
+ node TEXT NOT NULL,
29
+ manifest TEXT NOT NULL,
30
+ versions TEXT NOT NULL,
31
+ seen TEXT NOT NULL,
32
+ meta TEXT NOT NULL,
33
+ PRIMARY KEY (execution_id, ns, checkpoint_id)
34
+ );
35
+
36
+ CREATE INDEX idx_graph_checkpoint_parent ON graph_checkpoint(parent_checkpoint_id);
37
+
38
+ CREATE TABLE graph_step_write (
39
+ execution_id TEXT NOT NULL REFERENCES graph_execution(id) ON DELETE CASCADE,
40
+ ns TEXT NOT NULL DEFAULT '',
41
+ checkpoint_id TEXT NOT NULL,
42
+ task_id TEXT NOT NULL,
43
+ idx INTEGER NOT NULL,
44
+ channel TEXT NOT NULL,
45
+ value TEXT NOT NULL,
46
+ PRIMARY KEY (execution_id, ns, checkpoint_id, task_id, idx)
47
+ );
48
+
49
+ CREATE TABLE graph_interrupt (
50
+ execution_id TEXT NOT NULL REFERENCES graph_execution(id) ON DELETE CASCADE,
51
+ ns TEXT NOT NULL DEFAULT '',
52
+ interrupt_id TEXT NOT NULL,
53
+ node TEXT NOT NULL,
54
+ prompt TEXT NOT NULL,
55
+ security_class INTEGER NOT NULL DEFAULT 0,
56
+ resume_value TEXT,
57
+ resolved_by TEXT,
58
+ status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open','resolved','abandoned')),
59
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
60
+ resolved_at TEXT,
61
+ PRIMARY KEY (execution_id, ns, interrupt_id)
62
+ );
63
+
64
+ CREATE UNIQUE INDEX idx_graph_interrupt_one_open
65
+ ON graph_interrupt(execution_id, ns, node) WHERE status = 'open';
66
+
67
+ INSERT INTO schema_version (version) VALUES (14);
@@ -54,7 +54,7 @@ gate): **`ck-predict` at ADR** (5-persona pre-decision debate) and **`ck-scenari
54
54
  | UI card | `ui-ux-designer` | — | inline + `law/DESIGN.md` | review vs DESIGN.md |
55
55
  | Repair / diagnostic | `debugger` | — | inline root-cause + fresh same-ladder subagent | `harness intervention add` |
56
56
  | Review | `code-reviewer` (+ `typescript-reviewer` or `python-reviewer` layered — see language-specialist lens) | `bmad-code-review` (3-layer adversarial) | inline | `harness intervention add` on red |
57
- | Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story update --status implemented` |
57
+ | Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story complete --id … --proof-source manual` (never bare `update --status implemented`) |
58
58
  | Docs sync | `docs-manager` | — | inline doc update | — |
59
59
  | Verify-live | `tester` / `web-testing` | `bmad-qa-generate-e2e-tests` | curl/Playwright | `harness story update --e2e 1` + `trace` |
60
60
 
@@ -17,9 +17,14 @@ principles below. Operator chose: **Tier-A auto-merge green cards; halt at secur
17
17
  ```
18
18
  for each todo card in card-number order:
19
19
  0. tier-classify. If Tier-C -> HALT, write DEBT line, ask operator. Do not proceed.
20
+ [records: flow harness graph record --execution "$(flow.sh harness graph session)" \
21
+ --ns card:C-NNN --node card-review --interrupt --security-class]
20
22
  1. spawn ONE scoped subagent (agent-stage-mapping.md) in its own worktree
21
- git worktree add ../<project>-C-NNN -b card/C-NNN
23
+ /flow workspace add card/C-NNN --card C-NNN [records: card-dispatch]
24
+ (a raw `git worktree add` records NOTHING — use the verb, or the journal has a hole)
22
25
  2. agent builds to contract, touches only allowed files, runs ## Verify for real
26
+ [no recording: the build is LLM work, and checkpoints only sit at deterministic
27
+ gate/step boundaries — the next `check` is what proves it]
23
28
  3. review the diff (code-reviewer or bmad-code-review 3-layer; see adversarial-review.md).
24
29
  On a security-class card, add a USABLE cross-vendor lens (Codex, and/or Antigravity/Gemini-3).
25
30
  red (strike 1) -> repair: spawn Task(subagent_type="debugger") with a SCOPED BRIEF
@@ -32,12 +37,34 @@ for each todo card in card-number order:
32
37
  USABLE, else escalate
33
38
  green -> continue
34
39
  4. flow.sh check C-NNN must PASS (mechanical) + gate-rules semantic check
40
+ [records: card-review, with the gate exit as the evidence]
35
41
  5. merge to main in card order; deploy; VERIFY ON LIVE URL (merge != shipped)
36
42
  6. paste world-state evidence into the card; status: done
37
- 7. flow.sh harness story update --status implemented + trace; log AUTO-LOG.md
38
- 8. remove worktree
43
+ [records: card-verify-live, via '/flow card done C-NNN' (evidence gate = the done boundary)]
44
+ 7. flow.sh harness story complete --id … --proof-source manual (or card_markdown_gate) + trace; log AUTO-LOG.md
45
+ 8. remove worktree: /flow workspace remove card/C-NNN
46
+ [records: card-merge — the executor computes the ancestry proof itself and records
47
+ card-abandon instead if the branch was never merged — and ingests the worktree's
48
+ usage events into the main DB before the tree is gone]
39
49
  ```
40
50
 
51
+ ## Recording contract (graph executor, `FLOW_GRAPH_EXECUTOR=1`)
52
+ Boundaries are recorded by the verbs above, which this loop already calls — nothing depends
53
+ on the agent remembering an extra call. Only the Tier-C interrupt (step 0) is an explicit
54
+ `graph record` invocation; `graph session` returns the project's running execution
55
+ (minting one atomically if absent — one execution per project, shared by every worktree).
56
+ The card path is dispatch (`workspace add`) → review (`check`) → merge (`workspace remove`),
57
+ with an optional verify-live between review and merge when you close the card via
58
+ `card done` rather than hand-editing `status:`; an unmerged teardown records card-abandon,
59
+ and a RED review loops back to review (bounded by max_visits — two strikes, then escalate).
60
+ Every one of those transitions is a declared topology edge, and
61
+ `tests/test_flow_graph_parallel_cards.sh` drives a real lifecycle and fails if the runner
62
+ ever emits a transition the topology does not declare. The executor never drives the loop: it records evidence, computes
63
+ the next node from topology + journal, and reconciles against git (`graph resume` with no
64
+ open interrupt prints a reconciliation report), so merged-but-unrecorded work is never
65
+ re-dispatched. Ready/parallel decisions come from `flow harness graph cards`
66
+ (deps-met parity with `/flow ready`, plus allowed-files overlap serialization).
67
+
41
68
  ## Hard stops (mandatory — see loop-harness-2026-principles.md in Phase 4)
42
69
  - Iteration cap per card (e.g. 2 repair attempts), token budget, wall-clock cap. Exceed any
43
70
  -> HALT + report. A loop with no cap is an antipattern.
@@ -14,6 +14,7 @@ install dir; run from the project root so `flow/` and `cards/` resolve.
14
14
  | `/flow card start C-NNN` | `bash <skill>/runner/flow.sh card start C-NNN` | Optional: mark the card in flight (operator-visible in-progress, shown in `/flow status`). Portable `cards/.inflight` registry; does NOT touch the gated `status:` field. Coexists with hand-edit. |
15
15
  | `/flow card done C-NNN` | `bash <skill>/runner/flow.sh card done C-NNN` | Optional convenience: CLI-owned flip to `done`, gated by the SAME done-rules as `check` (reverts on fail — never a hollow done). Then still apply the semantic card review (diff-vs-scope, contract shapes, real evidence) as with `check`. |
16
16
  | `/flow check C-NNN` | `bash <skill>/runner/flow.sh check C-NNN` | If PASS mechanically: review diff-vs-scope, allowed-files drift, contract-shape match, DESIGN.md for UI, evidence = real world-state. |
17
+ | `/flow gate <stage>` (or `--card C-NNN`) | `bash <skill>/runner/flow.sh gate <stage\|--card C-NNN>` | Read-only mechanical scan (unchecked boxes + `[FILL]`) with NO unlock, NO template copy, NO durable writes — the only flow.sh verb the graph executor runs from topology `cmd` position. Relay findings; `next`/`check` still own real progression + durable hooks. |
17
18
  | `/flow contract` | `bash <skill>/runner/flow.sh contract` | After the contract gate / before UI cards (web): flags client base-URL vs served-path prefix drift (double-`/api`, mixed-prefix) — the class spec-diff tools miss. Advisory; confirm on the running app. |
18
19
  | `/flow tokens` | `bash <skill>/runner/flow.sh tokens` | On/after UI cards: flags DESIGN.md tokens the CSS never uses + VALUE mismatches (same name, drifted value) + orphan CSS vars (info). Advisory; if the swap is intentional, record a dated DESIGN.md amendment. |
19
20
  | `/flow coherence` | `bash <skill>/runner/flow.sh coherence` | Flags version drift across declared version fields (package.json / pyproject / src app_version). The cheap doc-vs-code slice; semantic contradictions stay a human gate-challenge. |
@@ -48,6 +48,7 @@ SKILL.md's Forbidden list). When a brand-new user's ask requires you to draft so
48
48
  - design
49
49
  - doctor
50
50
  - ready
51
+ - gate
51
52
 
52
53
  **Must-ask — confirm before running** (mutates state, costs money, or is
53
54
  operator-authority-only):
@@ -0,0 +1,146 @@
1
+ {
2
+ "$comment": "flow topology (source of truth once FLOW_GRAPH_EXECUTOR defaults on). Trusted data: loads ONLY from the skill install dir, pin-verified against harness/pins/flow-topology.sha256 (canonical-JSON sha256; regenerate via the command in harness/README.md). cmd is an argv ARRAY (no shell) restricted to the read-only `gate` verb; mutating verbs are lint-banned. Debt-skips are a plan_next traversal semantic (skipped gate = accepted-with-debt) - NO bypass edges belong here. EVERY card edge below is a transition the runner actually emits: dispatch (workspace add) -> review (check) -> merge (workspace remove), with an optional verify-live between review and merge when the operator uses `card done` instead of hand-editing status (workspace remove, executor-proved ancestry). A RED review loops back to review - the agent repairs and re-runs check, which records another review - bounded by max_visits (two strikes, then escalate per auto-run.md). card-abandon is the unmerged-teardown terminal and can follow any card node, because `workspace remove` may happen at any point. There is deliberately no card-build node: the build is LLM work with no deterministic boundary. Edit checklist: run `graph lint`, regenerate the pin, bump topology_version for semantic changes.",
3
+ "topology_version": 1,
4
+ "entry": [
5
+ "stage-00",
6
+ "card-dispatch"
7
+ ],
8
+ "nodes": {
9
+ "stage-00": {
10
+ "type": "gate_check",
11
+ "stage": "00-idea",
12
+ "cmd": [
13
+ "flow.sh",
14
+ "gate",
15
+ "00-idea"
16
+ ]
17
+ },
18
+ "stage-01": {
19
+ "type": "gate_check",
20
+ "stage": "01-research",
21
+ "cmd": [
22
+ "flow.sh",
23
+ "gate",
24
+ "01-research"
25
+ ]
26
+ },
27
+ "stage-02": {
28
+ "type": "gate_check",
29
+ "stage": "02-scope",
30
+ "cmd": [
31
+ "flow.sh",
32
+ "gate",
33
+ "02-scope"
34
+ ]
35
+ },
36
+ "stage-03": {
37
+ "type": "gate_check",
38
+ "stage": "03-prd",
39
+ "cmd": [
40
+ "flow.sh",
41
+ "gate",
42
+ "03-prd"
43
+ ]
44
+ },
45
+ "stage-04": {
46
+ "type": "gate_check",
47
+ "stage": "04-adr",
48
+ "cmd": [
49
+ "flow.sh",
50
+ "gate",
51
+ "04-adr"
52
+ ]
53
+ },
54
+ "stage-05": {
55
+ "type": "gate_check",
56
+ "stage": "05-contract",
57
+ "cmd": [
58
+ "flow.sh",
59
+ "gate",
60
+ "05-contract"
61
+ ]
62
+ },
63
+ "card-dispatch": {
64
+ "type": "record_evidence"
65
+ },
66
+ "card-review": {
67
+ "type": "gate_check",
68
+ "cmd": [
69
+ "flow.sh",
70
+ "gate",
71
+ "--card",
72
+ "{card}"
73
+ ],
74
+ "max_visits": 3
75
+ },
76
+ "card-verify-live": {
77
+ "type": "record_evidence"
78
+ },
79
+ "card-merge": {
80
+ "type": "git_op"
81
+ },
82
+ "card-abandon": {
83
+ "type": "record_evidence"
84
+ }
85
+ },
86
+ "edges": [
87
+ {
88
+ "from": "stage-00",
89
+ "to": "stage-01"
90
+ },
91
+ {
92
+ "from": "stage-01",
93
+ "to": "stage-02"
94
+ },
95
+ {
96
+ "from": "stage-02",
97
+ "to": "stage-03"
98
+ },
99
+ {
100
+ "from": "stage-03",
101
+ "to": "stage-04"
102
+ },
103
+ {
104
+ "from": "stage-04",
105
+ "to": "stage-05"
106
+ },
107
+ {
108
+ "from": "card-dispatch",
109
+ "to": "card-review"
110
+ },
111
+ {
112
+ "from": "card-review",
113
+ "to": "card-verify-live",
114
+ "when": "review_green"
115
+ },
116
+ {
117
+ "from": "card-verify-live",
118
+ "to": "card-merge"
119
+ },
120
+ {
121
+ "from": "card-review",
122
+ "to": "card-review",
123
+ "when": "review_red"
124
+ },
125
+ {
126
+ "from": "card-dispatch",
127
+ "to": "card-abandon",
128
+ "when": "review_red"
129
+ },
130
+ {
131
+ "from": "card-review",
132
+ "to": "card-abandon",
133
+ "when": "review_red"
134
+ },
135
+ {
136
+ "from": "card-verify-live",
137
+ "to": "card-abandon",
138
+ "when": "review_red"
139
+ },
140
+ {
141
+ "from": "card-review",
142
+ "to": "card-merge",
143
+ "when": "review_green"
144
+ }
145
+ ]
146
+ }