qfai 2.1.2 → 2.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.
- package/assets/defaults/agent-routing/qfai-implement.yml +9 -0
- package/assets/defaults/agent-routing/qfai-triage.yml +9 -0
- package/assets/defaults/workflows/answer-in-session.yml +8 -0
- package/assets/defaults/workflows/quarantine-flaky.yml +3 -0
- package/assets/defaults/workflows/repair-consistency.yml +1 -0
- package/assets/defaults/workflows/run-operation.yml +11 -0
- package/assets/defaults/workflows/write-procedure.yml +18 -0
- package/assets/graph/codegraph-native.mjs +174 -0
- package/assets/graph/graphify-native.py +72 -0
- package/assets/init/.claude/settings.json +2 -20
- package/assets/init/.codex/hooks.json +0 -12
- package/assets/init/.qfai/assistant/rule/constitution.md +31 -15
- package/assets/init/.qfai/assistant/rule/drift-protocol.md +62 -1
- package/assets/init/.qfai/assistant/rule/references/codex-delegation.md +27 -0
- package/assets/init/.qfai/assistant/rule/references/griller-recommendations.md +21 -9
- package/assets/init/.qfai/assistant/rule/review-convergence.md +25 -12
- package/assets/init/.qfai/assistant/rule/shared-skill-delegation-baseline.md +1 -0
- package/assets/init/.qfai/assistant/rule/shared-skill-operating-baseline.md +8 -3
- package/assets/init/.qfai/assistant/rule/workflow.md +2 -1
- package/assets/init/.qfai/assistant/skill/qfai-discussion/SKILL.md +6 -0
- package/assets/init/.qfai/assistant/skill/qfai-discussion/references/discussion-completion-matrix.md +18 -9
- package/assets/init/.qfai/assistant/skill/qfai-grilling/SKILL.md +30 -20
- package/assets/init/.qfai/assistant/skill/qfai-grilling/references/delegated-session.md +4 -3
- package/assets/init/.qfai/assistant/skill/qfai-grilling/references/session-record.md +8 -2
- package/assets/init/.qfai/assistant/skill/qfai-implement/SKILL.md +7 -0
- package/assets/init/.qfai/assistant/skill/qfai-implement/references/ui-affecting.md +2 -2
- package/assets/init/.qfai/assistant/skill/qfai-run/SKILL.md +41 -39
- package/assets/init/.qfai/assistant/skill/qfai-run/references/codex-cloud-install.md +80 -0
- package/assets/init/.qfai/assistant/skill/qfai-run/references/extraction.md +36 -25
- package/assets/init/.qfai/assistant/skill/qfai-run/references/operator-screens.md +9 -1
- package/assets/init/.qfai/assistant/skill/qfai-run/references/plan.md +2 -0
- package/assets/init/.qfai/assistant/skill/qfai-run/references/reached-step-observations.md +132 -0
- package/assets/init/.qfai/assistant/skill/qfai-run/scripts/codex-cloud-install.mjs +117 -0
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-authoring.md +20 -7
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-phase-checklists.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-pre-draft-grilling.md +4 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-quality-gate.md +1 -1
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-triage.md +29 -2
- package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md +36 -4
- package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/decisions.md +9 -0
- package/assets/init/.qfai/assistant/skill/qfai-triage/SKILL.md +10 -7
- package/assets/init/.qfai/assistant/skill/qfai-verify/references/verify-output-contract.md +7 -7
- package/assets/init/.qfai/assistant/step/common-design-md/STEP.md +33 -16
- package/assets/init/.qfai/assistant/step/common-gate-run/STEP.md +26 -0
- package/assets/init/.qfai/assistant/step/common-grilling-record/STEP.md +10 -2
- package/assets/init/.qfai/assistant/step/common-policy-check/STEP.md +1 -0
- package/assets/init/.qfai/assistant/step/common-review-cycle/STEP.md +23 -7
- package/assets/init/.qfai/assistant/step/discussion-interview/STEP.md +15 -8
- package/assets/init/.qfai/assistant/step/discussion-oq/STEP.md +5 -1
- package/assets/init/.qfai/assistant/step/discussion-pack/STEP.md +5 -1
- package/assets/init/.qfai/assistant/step/discussion-uiux/STEP.md +5 -1
- package/assets/init/.qfai/assistant/step/implement-dep-bump/STEP.md +8 -2
- package/assets/init/.qfai/assistant/step/implement-procedure/STEP.md +69 -0
- package/assets/init/.qfai/assistant/step/implement-quarantine/STEP.md +21 -4
- package/assets/init/.qfai/assistant/step/implement-regression-fix/STEP.md +3 -3
- package/assets/init/.qfai/assistant/step/implement-stress-harness/STEP.md +33 -2
- package/assets/init/.qfai/assistant/step/prototyping-preflight/STEP.md +8 -0
- package/assets/init/.qfai/assistant/step/sdd-triage/STEP.md +4 -2
- package/assets/init/.qfai/assistant/step/triage-answer/STEP.md +2 -2
- package/assets/init/.qfai/assistant/step/triage-operate/STEP.md +50 -0
- package/assets/init/.qfai/assistant/step/verify-qfai-gate/STEP.md +8 -1
- package/assets/init/.qfai/assistant/step/verify-repeat-run/STEP.md +40 -0
- package/assets/init/.qfai/assistant/step/verify-repo-gate/STEP.md +12 -0
- package/assets/init/root/.agents/rules/document-schema.md +17 -0
- package/assets/init/root/.agents/rules/documentation-clarity.md +1 -1
- package/assets/init/root/.agents/rules/graph-exploration.md +150 -0
- package/assets/init/root/.agents/rules/grilling.md +100 -43
- package/assets/init/root/.agents/rules/reminders.json +14 -18
- package/assets/init/root/.agents/rules/session-feedback.md +6 -11
- package/assets/init/root/.agents/rules/untrusted-content.md +2 -2
- package/assets/init/root/.agents/rules/user-questions.md +3 -3
- package/assets/init/root/.github/workflows/qfai-docs.yml +2 -2
- package/assets/init/root/.github/workflows/qfai-tests.yml +88 -35
- package/assets/init/root/.github/workflows/qfai-validate.yml +2 -2
- package/assets/init/root/AGENTS.md +1 -0
- package/assets/schemas/workflow/extraction.schema.json +6 -2
- package/assets/scripts/check-mdschema.mjs +77 -8
- package/dist/cli/index.cjs +2616 -1130
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +2617 -1121
- package/dist/cli/index.mjs.map +1 -1
- package/dist/graph/cli.cjs +1568 -0
- package/dist/graph/cli.cjs.map +1 -0
- package/dist/graph/cli.d.cts +9 -0
- package/dist/graph/cli.d.ts +9 -0
- package/dist/graph/cli.mjs +1541 -0
- package/dist/graph/cli.mjs.map +1 -0
- package/dist/graph/hook.cjs +817 -0
- package/dist/graph/hook.cjs.map +1 -0
- package/dist/graph/hook.d.cts +9 -0
- package/dist/graph/hook.d.ts +9 -0
- package/dist/graph/hook.mjs +792 -0
- package/dist/graph/hook.mjs.map +1 -0
- package/dist/index.cjs +2734 -818
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +129 -1
- package/dist/index.d.ts +129 -1
- package/dist/index.mjs +2707 -789
- package/dist/index.mjs.map +1 -1
- package/dist/process-C21nTvkT.d.cts +12 -0
- package/dist/process-C21nTvkT.d.ts +12 -0
- package/package.json +1 -1
|
@@ -216,6 +216,15 @@ routing:
|
|
|
216
216
|
rerun_policy: changed-scope-dependents
|
|
217
217
|
review_profile: default
|
|
218
218
|
|
|
219
|
+
- step: implement-procedure
|
|
220
|
+
phases:
|
|
221
|
+
- id: documentation
|
|
222
|
+
mandatory_agents: [doc-steward]
|
|
223
|
+
conditional_agents: []
|
|
224
|
+
parallel_groups: []
|
|
225
|
+
rerun_policy: changed-scope-dependents
|
|
226
|
+
review_profile: default
|
|
227
|
+
|
|
219
228
|
# Writes the bodies of empty acceptance tests, keeping each annotation.
|
|
220
229
|
- step: implement-acceptance
|
|
221
230
|
phases:
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
routing:
|
|
5
5
|
# The triage steps change no tracked file, so each has one author and no
|
|
6
6
|
# build or test phase.
|
|
7
|
+
- step: triage-operate
|
|
8
|
+
phases:
|
|
9
|
+
- id: operate
|
|
10
|
+
mandatory_agents: [devops-ci-engineer]
|
|
11
|
+
conditional_agents: []
|
|
12
|
+
parallel_groups: []
|
|
13
|
+
rerun_policy: changed-scope-dependents
|
|
14
|
+
review_profile: default
|
|
15
|
+
|
|
7
16
|
- step: triage-owned-cleanup
|
|
8
17
|
phases:
|
|
9
18
|
- id: cleanup
|
|
@@ -29,6 +29,9 @@ stages:
|
|
|
29
29
|
after: [note]
|
|
30
30
|
decisionPoints: []
|
|
31
31
|
branchPoints:
|
|
32
|
+
- step: implement-quarantine
|
|
33
|
+
outcomes:
|
|
34
|
+
- { outcome: failing-consistently, routes: decision-table }
|
|
32
35
|
- step: implement-diagnose
|
|
33
36
|
outcomes:
|
|
34
37
|
- { outcome: product-race, routes: [fix-intermittent] }
|
|
@@ -11,6 +11,7 @@ stages:
|
|
|
11
11
|
- { step: sdd-flow, passThrough: true }
|
|
12
12
|
- { step: sdd-story, passThrough: true }
|
|
13
13
|
- { step: sdd-contract, passThrough: true }
|
|
14
|
+
- { step: common-design-md, passThrough: true }
|
|
14
15
|
- { step: sdd-cycle, passThrough: true }
|
|
15
16
|
after: [diagnose]
|
|
16
17
|
review: spec
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
route: write-procedure
|
|
2
|
+
family: change
|
|
3
|
+
stages:
|
|
4
|
+
- id: documentation
|
|
5
|
+
kind: documentation
|
|
6
|
+
steps: [implement-procedure]
|
|
7
|
+
- id: note
|
|
8
|
+
kind: verify
|
|
9
|
+
steps:
|
|
10
|
+
- { step: verify-change-note, passThrough: true }
|
|
11
|
+
after: [documentation]
|
|
12
|
+
review: code
|
|
13
|
+
- id: verify
|
|
14
|
+
kind: verify
|
|
15
|
+
steps: [verify-qfai-gate, verify-repo-gate, verify-commit]
|
|
16
|
+
after: [note]
|
|
17
|
+
decisionPoints: []
|
|
18
|
+
branchPoints: []
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
const sdk = await import(pathToFileURL(process.argv[2]).href);
|
|
8
|
+
const { default: CodeGraph, setLogger, silentLogger } = sdk.default;
|
|
9
|
+
setLogger(silentLogger);
|
|
10
|
+
const mode = process.argv[4];
|
|
11
|
+
const corpus = process.argv[3];
|
|
12
|
+
const graph = mode
|
|
13
|
+
? await CodeGraph.open(process.argv[3], { readOnly: true })
|
|
14
|
+
: await CodeGraph.init(process.argv[3], { index: false });
|
|
15
|
+
try {
|
|
16
|
+
if (mode === "inventory") {
|
|
17
|
+
process.stdout.write(
|
|
18
|
+
JSON.stringify({ files: graph.getFiles().map((file) => file.path) }) + "\n",
|
|
19
|
+
);
|
|
20
|
+
} else if (mode === "coverage") {
|
|
21
|
+
process.stdout.write(JSON.stringify(await coverage(graph, corpus)) + "\n");
|
|
22
|
+
} else if (mode === undefined) {
|
|
23
|
+
const result = await graph.indexAll();
|
|
24
|
+
const build = graph.getIndexBuildInfo();
|
|
25
|
+
await writeFile(
|
|
26
|
+
path.join(corpus, ".codegraph", "qfai-native-evidence.json"),
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
engineVersion: build.version,
|
|
29
|
+
extractionVersion: build.extractionVersion,
|
|
30
|
+
lastIndexedAt: graph.getLastIndexedAt(),
|
|
31
|
+
result: {
|
|
32
|
+
success: result.success,
|
|
33
|
+
filesDiscovered: result.filesDiscovered ?? null,
|
|
34
|
+
filesIndexed: result.filesIndexed,
|
|
35
|
+
filesSkipped: result.filesSkipped,
|
|
36
|
+
filesErrored: result.filesErrored,
|
|
37
|
+
filesSkippedUnsupported: result.filesSkippedUnsupported ?? 0,
|
|
38
|
+
topUnsupportedExtensions: result.topUnsupportedExtensions ?? [],
|
|
39
|
+
errors: result.errors.map((error) => ({
|
|
40
|
+
code: error.code ?? "unclassified",
|
|
41
|
+
severity: error.severity,
|
|
42
|
+
filePath: safeRelativeInput(corpus, error.filePath),
|
|
43
|
+
})),
|
|
44
|
+
},
|
|
45
|
+
}) + "\n",
|
|
46
|
+
);
|
|
47
|
+
process.stdout.write("CodeGraph native indexing completed.\n");
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error("Unsupported native operation");
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
graph.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function safeRelativeInput(corpus, file) {
|
|
57
|
+
if (typeof file !== "string" || !file) return "Unidentified native input";
|
|
58
|
+
const relative = (path.isAbsolute(file) ? path.relative(corpus, file) : file).replaceAll(
|
|
59
|
+
"\\",
|
|
60
|
+
"/",
|
|
61
|
+
);
|
|
62
|
+
if (
|
|
63
|
+
path.posix.isAbsolute(relative) ||
|
|
64
|
+
path.win32.isAbsolute(relative) ||
|
|
65
|
+
relative.split("/").some((part) => !part || part === "." || part === "..") ||
|
|
66
|
+
/(?:^|\/)(?:\.env(?:\.|$)|\.npmrc$|\.pypirc$|\.netrc$|credentials?(?:\.|$)|secrets?(?:\.|$))|\.(?:pem|key|p12|pfx)$/i.test(
|
|
67
|
+
relative,
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return "Unidentified native input";
|
|
71
|
+
return relative;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function coverage(graph, corpus) {
|
|
75
|
+
const unknown = { complete: false, unsupported: [], unreadable: [], unparsed: [] };
|
|
76
|
+
let evidence;
|
|
77
|
+
try {
|
|
78
|
+
evidence = JSON.parse(
|
|
79
|
+
await readFile(path.join(corpus, ".codegraph", "qfai-native-evidence.json"), "utf8"),
|
|
80
|
+
);
|
|
81
|
+
} catch {
|
|
82
|
+
return unknown;
|
|
83
|
+
}
|
|
84
|
+
const build = graph.getIndexBuildInfo();
|
|
85
|
+
const result = evidence?.result;
|
|
86
|
+
const counts = [
|
|
87
|
+
"filesDiscovered",
|
|
88
|
+
"filesIndexed",
|
|
89
|
+
"filesSkipped",
|
|
90
|
+
"filesErrored",
|
|
91
|
+
"filesSkippedUnsupported",
|
|
92
|
+
];
|
|
93
|
+
if (
|
|
94
|
+
!record(evidence) ||
|
|
95
|
+
Object.keys(evidence).length !== 4 ||
|
|
96
|
+
evidence.engineVersion !== build.version ||
|
|
97
|
+
evidence.extractionVersion !== build.extractionVersion ||
|
|
98
|
+
!Number.isSafeInteger(evidence.lastIndexedAt) ||
|
|
99
|
+
evidence.lastIndexedAt !== graph.getLastIndexedAt() ||
|
|
100
|
+
!record(result) ||
|
|
101
|
+
Object.keys(result).length !== 8 ||
|
|
102
|
+
typeof result.success !== "boolean" ||
|
|
103
|
+
counts.some((key) => !Number.isSafeInteger(result[key]) || result[key] < 0) ||
|
|
104
|
+
!Array.isArray(result.errors) ||
|
|
105
|
+
!Array.isArray(result.topUnsupportedExtensions)
|
|
106
|
+
)
|
|
107
|
+
return unknown;
|
|
108
|
+
const unsupported = [];
|
|
109
|
+
const unreadable = [];
|
|
110
|
+
const unparsed = [];
|
|
111
|
+
let documentedInputs = 0;
|
|
112
|
+
for (const group of result.topUnsupportedExtensions) {
|
|
113
|
+
if (
|
|
114
|
+
!record(group) ||
|
|
115
|
+
typeof group.ext !== "string" ||
|
|
116
|
+
!Number.isSafeInteger(group.count) ||
|
|
117
|
+
group.count < 0
|
|
118
|
+
)
|
|
119
|
+
return unknown;
|
|
120
|
+
if (
|
|
121
|
+
/^\.(?:md|mdx|txt|rst|adoc|pdf|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|mp[34]|wav)$/i.test(
|
|
122
|
+
group.ext,
|
|
123
|
+
)
|
|
124
|
+
) {
|
|
125
|
+
documentedInputs += group.count;
|
|
126
|
+
} else {
|
|
127
|
+
unsupported.push(`${group.ext} (${group.count} inputs)`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const namedUnsupported = result.topUnsupportedExtensions.reduce(
|
|
131
|
+
(count, group) => count + group.count,
|
|
132
|
+
0,
|
|
133
|
+
);
|
|
134
|
+
if (namedUnsupported < result.filesSkippedUnsupported)
|
|
135
|
+
unsupported.push("Other unsupported native inputs");
|
|
136
|
+
if (
|
|
137
|
+
namedUnsupported > result.filesSkippedUnsupported ||
|
|
138
|
+
documentedInputs > result.filesSkippedUnsupported
|
|
139
|
+
)
|
|
140
|
+
return unknown;
|
|
141
|
+
for (const error of [
|
|
142
|
+
...result.errors,
|
|
143
|
+
...graph.getFiles().flatMap((file) => file.errors ?? []),
|
|
144
|
+
]) {
|
|
145
|
+
if (!record(error) || !["error", "warning"].includes(error.severity)) return unknown;
|
|
146
|
+
const file = safeRelativeInput(corpus, error.filePath);
|
|
147
|
+
if (error.code === "read_error") unreadable.push(file);
|
|
148
|
+
else unparsed.push(file);
|
|
149
|
+
}
|
|
150
|
+
if (result.filesErrored > 0 && unreadable.length === 0 && unparsed.length === 0)
|
|
151
|
+
unparsed.push("Unidentified native extraction errors");
|
|
152
|
+
const complete =
|
|
153
|
+
result.success &&
|
|
154
|
+
result.filesIndexed + result.filesSkipped + result.filesErrored >= result.filesDiscovered &&
|
|
155
|
+
result.filesErrored === 0 &&
|
|
156
|
+
unsupported.length === 0 &&
|
|
157
|
+
unreadable.length === 0 &&
|
|
158
|
+
unparsed.length === 0;
|
|
159
|
+
return {
|
|
160
|
+
complete,
|
|
161
|
+
unsupported,
|
|
162
|
+
unreadable: [...new Set(unreadable)],
|
|
163
|
+
unparsed: [...new Set(unparsed)],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function record(value) {
|
|
168
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
main().catch(() => {
|
|
172
|
+
process.stderr.write("CodeGraph native indexing failed.\n");
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Inspect the managed corpus with Graphify's native freshness operation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from graphify.detect import detect_incremental
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def input_inventory(output, graph):
|
|
11
|
+
manifest = json.loads((output / "manifest.json").read_text(encoding="utf-8"))
|
|
12
|
+
if not isinstance(manifest, dict):
|
|
13
|
+
raise ValueError("Invalid native manifest")
|
|
14
|
+
files = set(manifest.keys())
|
|
15
|
+
groups = [graph["nodes"]]
|
|
16
|
+
for name in ("edges", "links", "hyperedges"):
|
|
17
|
+
if name in graph:
|
|
18
|
+
groups.append(graph[name])
|
|
19
|
+
nested = graph.get("graph", {})
|
|
20
|
+
if not isinstance(nested, dict):
|
|
21
|
+
raise ValueError("Invalid native graph attributes")
|
|
22
|
+
if "hyperedges" in nested:
|
|
23
|
+
groups.append(nested["hyperedges"])
|
|
24
|
+
for group in groups:
|
|
25
|
+
if not isinstance(group, list):
|
|
26
|
+
raise ValueError("Invalid native graph references")
|
|
27
|
+
for item in group:
|
|
28
|
+
if not isinstance(item, dict):
|
|
29
|
+
raise ValueError("Invalid native graph reference")
|
|
30
|
+
for field in ("source_file", "definition_file", "path"):
|
|
31
|
+
value = item.get(field)
|
|
32
|
+
if value is None or value == "":
|
|
33
|
+
continue
|
|
34
|
+
if not isinstance(value, str):
|
|
35
|
+
raise ValueError("Invalid native input path")
|
|
36
|
+
files.add(value)
|
|
37
|
+
return sorted(files)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
root = Path(sys.argv[1])
|
|
42
|
+
kind = sys.argv[2]
|
|
43
|
+
output = root / "graphify-out"
|
|
44
|
+
graph = json.loads((output / "graph.json").read_text(encoding="utf-8"))
|
|
45
|
+
if not isinstance(graph, dict) or not isinstance(graph.get("nodes"), list):
|
|
46
|
+
raise ValueError("Invalid native graph")
|
|
47
|
+
if kind == "inventory":
|
|
48
|
+
print(json.dumps({"files": input_inventory(output, graph)}))
|
|
49
|
+
return
|
|
50
|
+
if kind not in ("semantic", "ast"):
|
|
51
|
+
raise ValueError("Unsupported native inspection")
|
|
52
|
+
state = detect_incremental(
|
|
53
|
+
root,
|
|
54
|
+
manifest_path=str(output / "manifest.json"),
|
|
55
|
+
kind=kind,
|
|
56
|
+
cache_root=output,
|
|
57
|
+
)
|
|
58
|
+
print(json.dumps({
|
|
59
|
+
"current": state["new_total"] == 0
|
|
60
|
+
and not state["deleted_files"]
|
|
61
|
+
and not state["excluded_files"],
|
|
62
|
+
"unsupported": state.get("unclassified", []),
|
|
63
|
+
"unreadable": state.get("unreadable", []),
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
try:
|
|
69
|
+
main()
|
|
70
|
+
except Exception:
|
|
71
|
+
sys.stderr.write("Graphify freshness inspection failed.\n")
|
|
72
|
+
sys.exit(1)
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"command": "node",
|
|
31
31
|
"args": [
|
|
32
32
|
"-e",
|
|
33
|
-
"try{const f=require('fs');let i={};try{i=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}const p=i.prompt;if(!(typeof p==='string'&&/^[ \\t]*(<(task-notification|wake)[\\s>]|\\[SYSTEM NOTIFICATION)/m.test(p))){let n=1;if(typeof i.session_id==='string'){try{const t=require('path').join(require('os').tmpdir(),['qfai-reminder',i.session_id,i.agent_id||'main',process.argv[2]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(process.argv[1],'utf8'))[process.argv[2]];if(e!==undefined){const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}",
|
|
33
|
+
"try{const f=require('fs');let i={};try{i=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}const p=i.prompt;if(!(typeof p==='string'&&/^[ \\t]*(<(task-notification|wake|ci-monitor-event)[\\s>]|\\[SYSTEM NOTIFICATION)/m.test(p))){let n=1;if(typeof i.session_id==='string'){try{const t=require('path').join(require('os').tmpdir(),['qfai-reminder',i.session_id,i.agent_id||'main',process.argv[2]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(process.argv[1],'utf8'))[process.argv[2]];if(e!==undefined){const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}",
|
|
34
34
|
"${CLAUDE_PROJECT_DIR}/.agents/rules/reminders.json",
|
|
35
35
|
"structured-question"
|
|
36
36
|
]
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"command": "node",
|
|
47
47
|
"args": [
|
|
48
48
|
"-e",
|
|
49
|
-
"try{const f=require('fs');let i={};try{i=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}const p=i.prompt;if(!(typeof p==='string'&&/^[ \\t]*(<(task-notification|wake)[\\s>]|\\[SYSTEM NOTIFICATION)/m.test(p))){let n=1;if(typeof i.session_id==='string'){try{const t=require('path').join(require('os').tmpdir(),['qfai-reminder',i.session_id,i.agent_id||'main',process.argv[2]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(process.argv[1],'utf8'))[process.argv[2]];if(e!==undefined){const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}",
|
|
49
|
+
"try{const f=require('fs');let i={};try{i=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}const p=i.prompt;if(!(typeof p==='string'&&/^[ \\t]*(<(task-notification|wake|ci-monitor-event)[\\s>]|\\[SYSTEM NOTIFICATION)/m.test(p))){let n=1;if(typeof i.session_id==='string'){try{const t=require('path').join(require('os').tmpdir(),['qfai-reminder',i.session_id,i.agent_id||'main',process.argv[2]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(process.argv[1],'utf8'))[process.argv[2]];if(e!==undefined){const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}",
|
|
50
50
|
"${CLAUDE_PROJECT_DIR}/.agents/rules/reminders.json",
|
|
51
51
|
"free-text-entry"
|
|
52
52
|
]
|
|
@@ -198,24 +198,6 @@
|
|
|
198
198
|
}
|
|
199
199
|
]
|
|
200
200
|
}
|
|
201
|
-
],
|
|
202
|
-
"Stop": [
|
|
203
|
-
{
|
|
204
|
-
"matcher": "*",
|
|
205
|
-
"hooks": [
|
|
206
|
-
{
|
|
207
|
-
"type": "command",
|
|
208
|
-
"statusMessage": "QFAI session feedback reminder",
|
|
209
|
-
"command": "node",
|
|
210
|
-
"args": [
|
|
211
|
-
"-e",
|
|
212
|
-
"try{const f=require('fs');let i;try{i=JSON.parse(f.readFileSync(0,'utf8'))}catch{process.exit(0)}const l=typeof i.last_assistant_message==='string'?i.last_assistant_message.trim():'';if(i.stop_hook_active===true||(l.length>0&&('?'+String.fromCharCode(65311)).includes(l.slice(-1))))process.exit(0);const m=JSON.parse(f.readFileSync(process.argv[1],'utf8'))[process.argv[2]];if(m!==undefined)console.log(JSON.stringify(m))}catch{}",
|
|
213
|
-
"${CLAUDE_PROJECT_DIR}/.agents/rules/reminders.json",
|
|
214
|
-
"session-feedback"
|
|
215
|
-
]
|
|
216
|
-
}
|
|
217
|
-
]
|
|
218
|
-
}
|
|
219
201
|
]
|
|
220
202
|
}
|
|
221
203
|
}
|
|
@@ -107,18 +107,6 @@
|
|
|
107
107
|
}
|
|
108
108
|
]
|
|
109
109
|
}
|
|
110
|
-
],
|
|
111
|
-
"Stop": [
|
|
112
|
-
{
|
|
113
|
-
"hooks": [
|
|
114
|
-
{
|
|
115
|
-
"type": "command",
|
|
116
|
-
"statusMessage": "QFAI session feedback reminder",
|
|
117
|
-
"command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let i;try{i=JSON.parse(f.readFileSync(0,'utf8'))}catch{process.exit(0)}const l=typeof i.last_assistant_message==='string'?i.last_assistant_message.trim():'';if(i.stop_hook_active===true||(l.length>0&&('?'+String.fromCharCode(65311)).includes(l.slice(-1))))process.exit(0);const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}catch{}\" session-feedback",
|
|
118
|
-
"timeout": 10
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
110
|
]
|
|
123
111
|
}
|
|
124
112
|
}
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
This document defines **non‑negotiable operating rules** for QFAI agents and subagents.
|
|
4
4
|
It is inspired by proven “constitution / articles / guardrails” patterns in existing SDD toolchains, but adapted to QFAI’s minimal workflow.
|
|
5
5
|
|
|
6
|
+
Actual user instructions take priority over repository guidelines. Recorded
|
|
7
|
+
applicable user authority is defined in
|
|
8
|
+
`.agents/rules/grilling.md#explicit-delegation-for-a-discussion`: an explicit
|
|
9
|
+
in-scope delegation may authorize reversible requested judgment without a
|
|
10
|
+
fictional individual approval answer. It supplies no unknown fact or authority
|
|
11
|
+
for an operation outside its scope.
|
|
12
|
+
|
|
6
13
|
Inside an `npx qfai workflow` run, what authorizes the run's work and the one target a work order binds its stage to are stated in `.qfai/assistant/rule/shared-skill-operating-baseline.md#what-authorizes-a-runs-work`. They add to these articles and except none.
|
|
7
14
|
|
|
8
15
|
---
|
|
@@ -260,7 +267,7 @@ sanctioned move, and neither is asking a sixth clarification anyway.
|
|
|
260
267
|
**A grilling session survives exhaustion too**, and for the same reason: its
|
|
261
268
|
questions never spent budget, so there is none left to run out of. A session
|
|
262
269
|
already under way continues to its own end condition, a delegated one still
|
|
263
|
-
|
|
270
|
+
requiring actual user authority for critical decisions, and a stage reached after
|
|
264
271
|
exhaustion still opens one where its work calls for it. Treating a spent budget
|
|
265
272
|
as the end of a session would put the design decisions back where this article
|
|
266
273
|
found them — settled quietly, on an assumption nobody was asked about.
|
|
@@ -322,16 +329,18 @@ If confidence is low, inspect the repository further.
|
|
|
322
329
|
|
|
323
330
|
**Stop on detection.** Where a contradiction in the spec, an unconsidered case,
|
|
324
331
|
or a technical obstacle surfaces mid-run, stop rather than deciding alone.
|
|
325
|
-
Settle it between the agents
|
|
326
|
-
|
|
327
|
-
|
|
332
|
+
Settle it between the agents. A critical decision needs an actual user answer
|
|
333
|
+
or recorded applicable authorization under
|
|
334
|
+
`.agents/rules/grilling.md#explicit-delegation-for-a-discussion`; without it,
|
|
335
|
+
ask when permitted or register it open. A contradiction with the spec is
|
|
336
|
+
critical. Nothing is recorded beyond the stage's final report.
|
|
328
337
|
|
|
329
338
|
**Only one outcome is the Drift Protocol's.**
|
|
330
339
|
|
|
331
|
-
| The stop concludes | What follows
|
|
332
|
-
| ---------------------------------------------------------------------------------------- |
|
|
333
|
-
| Settled input must change | `.qfai/assistant/rule/drift-protocol.md`: stop
|
|
334
|
-
| The obstacle is this run's to solve — an unavailable dependency, an approach that failed | The run solves it. Nothing upstream changes, so there is nothing to approve
|
|
340
|
+
| The stop concludes | What follows |
|
|
341
|
+
| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
342
|
+
| Settled input must change | `.qfai/assistant/rule/drift-protocol.md`: stop dependent work, raise the Change Request, record actual applicable user authority before changing it |
|
|
343
|
+
| The obstacle is this run's to solve — an unavailable dependency, an approach that failed | The run solves it. Nothing upstream changes, so there is nothing to approve |
|
|
335
344
|
|
|
336
345
|
Stopping does not change settled input and is not a second way to. What it
|
|
337
346
|
contributes to a Change Request is what that protocol asks of the class: for
|
|
@@ -366,7 +375,9 @@ rule master names.
|
|
|
366
375
|
Rules:
|
|
367
376
|
|
|
368
377
|
0. **A no-question mode is read first.** Where rule 4 applies, no question is put
|
|
369
|
-
|
|
378
|
+
to the user, so there is nothing whose availability rules 1 to 3 could judge.
|
|
379
|
+
Actual qualifying explicit delegation may still permit agent-to-agent rounds
|
|
380
|
+
under the master; no-question mode alone does not. Read
|
|
370
381
|
the other way round, a mode that withholds the tool would make it unavailable,
|
|
371
382
|
route the question to rule 3's plain-text fallback, and hand the agent a rule
|
|
372
383
|
that says to ask beside one that says not to.
|
|
@@ -406,12 +417,14 @@ Rules:
|
|
|
406
417
|
this rule is activated by the `--auto` flag alone.
|
|
407
418
|
6. **A grilling session does not reach the user under `--auto`.** Its questions
|
|
408
419
|
are exempt from the Article VI budget, not from rule 4: a no-question mode
|
|
409
|
-
|
|
420
|
+
sends no question to the user, whatever it is for. The session still runs — it
|
|
410
421
|
settles what the repository settles, dispatches sub-agents for the facts, and
|
|
411
|
-
in a delegated session adopts the griller's recommendation for
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
422
|
+
in a delegated session adopts the griller's recommendation for non-critical
|
|
423
|
+
decisions. A critical decision needs an actual user answer or recorded
|
|
424
|
+
applicable authorization; without it the node stays open. It opens every **node**
|
|
425
|
+
left over, including uncovered critical decisions and unavailable user-held
|
|
426
|
+
facts, **as a question in the register the stage reads**, so the
|
|
427
|
+
stage cannot complete over it. Every node, not every
|
|
415
428
|
decision: a fact only the user holds cannot be settled from evidence either,
|
|
416
429
|
and opening the decisions while dropping the facts loses exactly the nodes no
|
|
417
430
|
lookup could have reached. Where a document requires the field to hold
|
|
@@ -419,7 +432,10 @@ Rules:
|
|
|
419
432
|
open question; a labelled value under an open question is not a settled
|
|
420
433
|
decision. What rule 4 does not license is the assumption **alone**, which
|
|
421
434
|
reads as settled to whoever finds it next. A fact declared undefaultable has
|
|
422
|
-
no value to write down at all: the run stops and names it.
|
|
435
|
+
no value to write down at all: the run stops and names it. A qualifying
|
|
436
|
+
explicitly delegated session ends `adopted` only with the master's empty-tree,
|
|
437
|
+
actual-input and authority conditions met. No-question mode alone cannot
|
|
438
|
+
close a node or authorize a reserved decision.
|
|
423
439
|
|
|
424
440
|
This article survives context compaction because `.qfai/assistant/rule/constitution.md` is a P1 reload target.
|
|
425
441
|
|
|
@@ -24,6 +24,10 @@ The sole Approach-change exception is the published decision prefix recovery
|
|
|
24
24
|
below. It preserves the target's ID, Content, Status and complete original
|
|
25
25
|
Approach suffix. It excludes inherited rows and open-question rows.
|
|
26
26
|
|
|
27
|
+
The sole row-removal exception is the merge-added identical decision copy
|
|
28
|
+
recovery below. It removes only proven raw-identical surplus copies of a
|
|
29
|
+
decision and keeps one original. It excludes open-question rows.
|
|
30
|
+
|
|
27
31
|
## Allowed exceptions (minimal whitelist)
|
|
28
32
|
|
|
29
33
|
- A project may add a local overlay beside a shipped rule. The overlay adds project guidance; it does not repeal a shipped rule.
|
|
@@ -81,6 +85,60 @@ recovery authority and clear no form error. Inherited malformed records need a
|
|
|
81
85
|
separate owning change request; this operation cannot repair them. These are
|
|
82
86
|
owner instructions, not an automatic recovery command or a concurrency guarantee.
|
|
83
87
|
|
|
88
|
+
## Merge-added identical decision copy recovery
|
|
89
|
+
|
|
90
|
+
The SDD owner may manually remove only raw-identical surplus copies of a
|
|
91
|
+
decision row that an exact two-parent bad merge introduced. Approval of this
|
|
92
|
+
protocol authorizes no target. First append a separate new in-force change
|
|
93
|
+
request that names the exact `decisions.md` path and the decision ID.
|
|
94
|
+
|
|
95
|
+
1. Name the bad merge. It must be an available ancestor of current HEAD with
|
|
96
|
+
exactly two parents, and it must expose the introduced surplus. Name both
|
|
97
|
+
parents and their unique common ancestor. Each of the three must hold exactly
|
|
98
|
+
one byte-identical original of the row. Fix the explicit base and its unique
|
|
99
|
+
comparison ancestor; both must hold one original and no surplus selected for
|
|
100
|
+
removal. A missing or non-ancestor merge, a third parent, a non-unique
|
|
101
|
+
ancestor or a source that does not hold exactly one original stops recovery.
|
|
102
|
+
2. Read the complete stored UTF-8 raw row of every selected current occurrence:
|
|
103
|
+
all four cells, delimiters, padding and line ending. Hash each with SHA-256,
|
|
104
|
+
including the line ending. Do not trim, decode escapes or normalize endings.
|
|
105
|
+
Nonidentical cells, padding or endings are a different collision and are
|
|
106
|
+
outside this recovery.
|
|
107
|
+
3. Record in the request the path, the ID, the bad merge, both parents, the
|
|
108
|
+
ancestor, the base and its comparison ancestor, the location of each
|
|
109
|
+
occurrence, the raw hash, the retain/remove mapping, the actual authority,
|
|
110
|
+
the residual risk and the rollback. Record actual authority; invent no
|
|
111
|
+
answer. The request fixes input lineage only.
|
|
112
|
+
4. After the request is appended, have the evidence for the write reviewed
|
|
113
|
+
independently and adopted. It fixes the actual write HEAD, base and
|
|
114
|
+
comparison ancestor, the complete preimage and replacement bytes and hashes,
|
|
115
|
+
the mapping and the current authority. The request does not name its own
|
|
116
|
+
commit, so the actual write revision stays distinct from the lineage above.
|
|
117
|
+
This adds no receipt grammar and no validator input.
|
|
118
|
+
5. Immediately before writing, recheck the refs, their availability, the path,
|
|
119
|
+
the authority, the occurrence count, the mapping and the bytes. Stale,
|
|
120
|
+
missing, ambiguous, conflicting or changed proof stops before writing.
|
|
121
|
+
Changed observations need refreshed reviewed evidence. A changed target or
|
|
122
|
+
operation needs a new request; do not rewrite the cells of an earlier one.
|
|
123
|
+
6. Remove only the mapped surplus occurrences. Keep one complete original, every
|
|
124
|
+
distinct decision and reference, every unrelated current byte and the Git
|
|
125
|
+
history. Do not change a Status, rewrite a reference or reorder other
|
|
126
|
+
history. Open-question rows, nonidentical collisions, ordinary duplicate
|
|
127
|
+
cleanup and surplus that the base or its comparison ancestor already holds
|
|
128
|
+
are excluded.
|
|
129
|
+
7. Rollback restores the recorded complete preimage only while the current file
|
|
130
|
+
is byte-identical to its owned replacement. Otherwise keep the current bytes,
|
|
131
|
+
stop and name the unrestored path. Restoring brings back the duplicate
|
|
132
|
+
failure. Do not reset, amend or force push.
|
|
133
|
+
|
|
134
|
+
The existing duplicate check, positional drift, consumed-DONE authorization and
|
|
135
|
+
valid Status-only rules keep applying without an exemption. Other drift that
|
|
136
|
+
the file already has stays diagnosed; it is not a prerequisite for this
|
|
137
|
+
correction. An actual required stage gate still stops dependent work. Correcting
|
|
138
|
+
the copies is not a whole-target PASS, and the request stays at WIP while a named
|
|
139
|
+
action or required check is unfinished. These are owner instructions, not a
|
|
140
|
+
command, an executor or a concurrency, atomicity or power-loss guarantee.
|
|
141
|
+
|
|
84
142
|
## Drift classes
|
|
85
143
|
|
|
86
144
|
Intent drift means an approved obligation should change. Record the current obligation, the proposed behavior, realistic
|
|
@@ -168,6 +226,9 @@ copy of a locked file before deciding its lock is wrong.
|
|
|
168
226
|
## Non-negotiable constraints
|
|
169
227
|
|
|
170
228
|
- Downstream stages do not patch protected upstream artifacts before an in-force Change request: decision row authorizes the path. The drift profile compares the branch against baseBranch and reports QFAI-DRIFT-001 for an unapproved protected change. The SDD owner may create the request row itself without a prior row.
|
|
171
|
-
-
|
|
229
|
+
QFAI-DRIFT-003 is a warning that nothing was compared, because baseBranch does not resolve.
|
|
230
|
+
QFAI-DRIFT-004 is a warning that an uncommitted edit to a protected file is not compared:
|
|
231
|
+
record the Change request row before you commit the change.
|
|
232
|
+
- Existing decisions.md and open-questions.md rows retain their ID, Content, and Approach, except for the explicit branch-added decision renumbering, published decision prefix recovery and merge-added identical decision copy recovery above. Inherited rows retain those cells without exception. A former row may change Status; new content is appended as a new row.
|
|
172
233
|
- Vendored assistant rules are changed in the package and synchronized into projects by `npx qfai init --force`, which overwrites a local edit. A project rule lives in a `*.local.md` overlay beside the vendored one.
|
|
173
234
|
- When approval is unavailable, keep the affected items stopped and report the decision needed. Continue unrelated work.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Codex Delegation
|
|
2
|
+
|
|
3
|
+
Read before the first `spawn_agent` call when the host is Codex.
|
|
4
|
+
|
|
5
|
+
## Codex delegation notes
|
|
6
|
+
|
|
7
|
+
A sub-agent should cost the orchestrator one brief and one final answer. A call
|
|
8
|
+
that only checks progress, such as a short wait, a sleep or an agent listing,
|
|
9
|
+
is one more model call, so the orchestrator makes none.
|
|
10
|
+
|
|
11
|
+
- **Self-contained brief.** Call `spawn_agent` with `fork_turns: "none"` and a
|
|
12
|
+
`message` that holds everything the sub-agent needs: the task, the files to
|
|
13
|
+
read, the paths it may write, the checks to run and the shape of its answer.
|
|
14
|
+
`fork_turns` defaults to `"all"`, which copies the whole parent thread into
|
|
15
|
+
the sub-agent. Where the tool takes `fork_context` instead, leave it out or
|
|
16
|
+
pass `false`.
|
|
17
|
+
- **One final answer, no interim messages.** The brief tells the sub-agent to
|
|
18
|
+
send no `send_message` updates and to return its result once, as its final
|
|
19
|
+
answer. Where `wait_agent` takes no `targets`, every queued message ends the
|
|
20
|
+
orchestrator's wait early.
|
|
21
|
+
- **Long wait.** With no work of its own left, the orchestrator calls
|
|
22
|
+
`wait_agent` with a `timeout_ms` of minutes within the maximum the tool states,
|
|
23
|
+
such as `600000`, not 10 to 60 seconds. A wait that times out is
|
|
24
|
+
followed by another long wait.
|
|
25
|
+
- **No polling.** Never wait with `sleep`, a loop of short `wait_agent` calls or
|
|
26
|
+
repeated `list_agents` calls. `followup_task` gives a sub-agent a new task; it
|
|
27
|
+
never asks for progress.
|
|
@@ -6,15 +6,27 @@ Read when a griller's recommendation is used.
|
|
|
6
6
|
|
|
7
7
|
A grilling session puts a recommended answer beside each question (`.agents/rules/grilling.md`). Who settled the decision decides what happens next.
|
|
8
8
|
|
|
9
|
-
| The decision was settled
|
|
10
|
-
|
|
|
11
|
-
| By the user, from the recommendation among the inputs
|
|
12
|
-
| Agent to agent,
|
|
13
|
-
| Agent to agent, critical,
|
|
14
|
-
|
|
15
|
-
**The second row is how a delegated session is meant to end**
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
| The decision was settled | What follows |
|
|
10
|
+
| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
11
|
+
| By the user, from the recommendation among the inputs | The decision is theirs. The griller may review the artifact |
|
|
12
|
+
| Agent to agent, non-critical or with recorded applicable human authority | The decision stands. The griller that recommended it does not review the artifact |
|
|
13
|
+
| Agent to agent, critical, without an actual user answer or recorded applicable authority | The artifact is wrong, and no reviewer can clear it |
|
|
14
|
+
|
|
15
|
+
**The second row is how a delegated session is meant to end**
|
|
16
|
+
(`.agents/rules/grilling.md`), not a finding. The reviewer checks the final
|
|
17
|
+
report and, for a critical decision, the actual
|
|
18
|
+
applicable user authority defined in
|
|
19
|
+
`.agents/rules/grilling.md#explicit-delegation-for-a-discussion`.
|
|
20
|
+
An explicit delegation covering reversible requested judgment is authority,
|
|
21
|
+
not an individual human option answer. The recommending griller remains
|
|
22
|
+
disqualified from reviewing an agent adoption. A reviewer that doubts its merit
|
|
23
|
+
raises an ordinary finding against the artifact under its own remit.
|
|
24
|
+
|
|
25
|
+
**The third row is not a routing problem.** Without an actual answer or
|
|
26
|
+
recorded applicable user authority, a critical decision stays open. A run that
|
|
27
|
+
cannot ask records it as an open question rather than adopting it. Agent
|
|
28
|
+
agreement cannot supply authority, and a different reviewer cannot repair its
|
|
29
|
+
absence. No-question mode alone authorizes no decision.
|
|
18
30
|
The reviewer must return `REVISE` and name the decision: it is reopened and put to the user, or recorded open where no question can be asked.
|
|
19
31
|
|
|
20
32
|
The first row needs a reason, because the intuitive one is wrong. A sub-agent starting with a reset context cannot defer to something it does not remember, so deference is not the risk. **Correlation** is: a fresh instance of the same agent, on the same model, over the same evidence, re-derives the preference that produced the recommendation and finds it good on the merits. Resetting the
|