@agentsfleet/orly 0.10.13 → 0.11.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/.githooks/pre-push +2 -1
- package/AGENTS.md +21 -31
- package/README.md +65 -29
- package/SOUL.md +13 -23
- package/audits/agents-md.md +67 -57
- package/audits/deinit-pairs.sh +13 -18
- package/audits/design-tokens.sh +9 -23
- package/audits/doc-read.sh +10 -33
- package/audits/doc-reads.ts +41 -0
- package/audits/error-codes.sh +18 -25
- package/audits/logging-rust.awk +145 -0
- package/audits/logging.sh +10 -166
- package/audits/msid-ui.sh +45 -18
- package/audits/rule-ledger-lib.sh +9 -6
- package/audits/rust-error.sh +20 -15
- package/audits/scope.sh +88 -0
- package/audits/spec-template.sh +2 -2
- package/audits/spec-template.ts +1 -1
- package/audits/sql-mod.sh +11 -14
- package/audits/ufs-strings.awk +164 -0
- package/audits/ufs.sh +10 -188
- package/core/operating-model.md +8 -8
- package/dispatch/edit_rules.md +8 -6
- package/dispatch/lib.sh +41 -26
- package/dispatch/lifecycle.md +18 -6
- package/dispatch/name_architecture.md +8 -3
- package/dispatch/verify.md +1 -1
- package/dispatch/write_any.md +10 -3
- package/dispatch/write_changelog.md +7 -4
- package/dispatch/write_go.md +1 -1
- package/dispatch/write_http.md +5 -7
- package/dispatch/write_python.md +1 -1
- package/dispatch/write_rust.md +17 -1
- package/dispatch/write_shell.md +1 -1
- package/dispatch/write_spec.md +3 -1
- package/dispatch/write_sql.md +28 -6
- package/dispatch/write_ts_adhere_bun.md +11 -0
- package/dispatch/write_zig.md +53 -21
- package/docs/CHANGELOG_VOICE.md +11 -2
- package/docs/EXECUTE_DOC_READS.md +10 -10
- package/docs/HARNESS_VERIFY_OUTPUT.md +6 -3
- package/docs/JUDGMENTS.md +153 -0
- package/docs/LIFECYCLE_PATTERNS.md +26 -16
- package/docs/LOGGING_STANDARD.md +39 -19
- package/docs/ORLY_ARCHITECTURE.md +77 -6
- package/docs/RELEASE_TEMPLATE.md +21 -5
- package/docs/REST_API_DESIGN_GUIDELINES.md +86 -21
- package/docs/RULE_ENFORCEMENT.md +6 -6
- package/docs/SCHEMA_CONVENTIONS.md +24 -3
- package/docs/TEMPLATE.md +4 -4
- package/docs/VERIFY_TIERS.md +2 -2
- package/docs/greptile-learnings/RULES.md +81 -26
- package/llms.txt +37 -34
- package/package.json +6 -1
- package/packs/language/go/rules.md +1 -1
- package/packs/language/python/rules.md +1 -1
- package/packs/language/rust/rules.md +17 -1
- package/packs/language/shell/rules.md +1 -1
- package/registry.json +22 -7
- package/schemas/profile.schema.json +48 -5
- package/skills/orly-babysit-prs/SKILL.md +87 -34
- package/skills/orly-spec-new/SKILL.md +19 -6
- package/skills/orly-write-integration-test/SKILL.md +4 -4
- package/skills/orly-write-unit-test/SKILL.md +10 -1
- package/src/cli.test.ts +2 -2
- package/src/cli.ts +19 -75
- package/src/cli_help.ts +69 -0
- package/src/command_limits.ts +20 -0
- package/src/command_process.test.ts +109 -0
- package/src/command_process.ts +88 -0
- package/src/command_runner.test.ts +88 -0
- package/src/command_runner.ts +33 -0
- package/src/comprehension_cache.test.ts +123 -0
- package/src/config.test.ts +5 -4
- package/src/config.ts +26 -164
- package/src/config_discovery.ts +159 -0
- package/src/config_validation.test.ts +56 -0
- package/src/config_validation.ts +25 -0
- package/src/criteria.test.ts +1 -1
- package/src/criteria.ts +10 -26
- package/src/criteria_spec.ts +12 -20
- package/src/criteria_support.ts +2 -19
- package/src/doc_reads.test.ts +42 -0
- package/src/doc_rules.ts +2 -2
- package/src/evaluation_resources.test.ts +64 -0
- package/src/execution/config.ts +22 -0
- package/src/execution/plan.test.ts +65 -0
- package/src/execution/plan.ts +51 -0
- package/src/gates.ts +39 -24
- package/src/gates_spec.test.ts +110 -4
- package/src/gates_test_support.ts +9 -7
- package/src/install.test.ts +38 -222
- package/src/install.ts +117 -308
- package/src/install_loaders.test.ts +203 -0
- package/src/install_nested.test.ts +2 -2
- package/src/install_packs.test.ts +66 -6
- package/src/installation/hooks.ts +63 -0
- package/src/installation/lock.test.ts +97 -0
- package/src/installation/lock.ts +56 -0
- package/src/installation/migration.test.ts +212 -0
- package/src/installation/migration.ts +65 -0
- package/src/installation/plan.ts +96 -0
- package/src/installation/transaction.ts +126 -0
- package/src/installation_preservation.test.ts +48 -0
- package/src/judgments/advice.test.ts +38 -0
- package/src/judgments/advice.ts +32 -0
- package/src/judgments/command.integration.test.ts +47 -0
- package/src/judgments/command.ts +90 -0
- package/src/judgments/constants.ts +55 -0
- package/src/judgments/evidence.test.ts +48 -0
- package/src/judgments/evidence.ts +34 -0
- package/src/judgments/files.test.ts +62 -0
- package/src/judgments/files.ts +73 -0
- package/src/judgments/questions.test.ts +19 -0
- package/src/judgments/questions.ts +85 -0
- package/src/judgments/replay.test.ts +43 -0
- package/src/judgments/replay.ts +66 -0
- package/src/judgments/runner.integration.test.ts +165 -0
- package/src/judgments/runner.ts +97 -0
- package/src/judgments/scan.test.ts +19 -0
- package/src/judgments/scan.ts +40 -0
- package/src/judgments/syntax.test.ts +115 -0
- package/src/judgments/syntax.ts +71 -0
- package/src/judgments/syntax_worker.ts +70 -0
- package/src/judgments/test_support.ts +60 -0
- package/src/judgments/transport.integration.test.ts +63 -0
- package/src/judgments/transport.ts +35 -0
- package/src/judgments/types.test.ts +43 -0
- package/src/judgments/types.ts +66 -0
- package/src/judgments/wire.test.ts +48 -0
- package/src/judgments/wire.ts +40 -0
- package/src/loaders.test.ts +13 -16
- package/src/loaders.ts +28 -10
- package/src/model.test.ts +20 -3
- package/src/model.ts +7 -18
- package/src/pack_hygiene.test.ts +42 -1
- package/src/pack_hygiene.ts +18 -8
- package/src/references.ts +15 -7
- package/src/release_inventory.test.ts +80 -0
- package/src/render.test.ts +1 -1
- package/src/render.ts +3 -8
- package/src/required_scanner.test.ts +36 -0
- package/src/review_poll.test.ts +158 -0
- package/src/scanner_scope.test.ts +202 -0
- package/src/setup.test.ts +9 -6
- package/src/spec_baseline.test.ts +1 -1
- package/src/spec_completion.test.ts +74 -0
- package/src/spec_obligations.ts +64 -0
- package/src/spec_rehearsal.test.ts +182 -0
- package/src/spec_template_io.test.ts +11 -9
- package/src/surfaces.test.ts +2 -0
- package/src/surfaces.ts +1 -1
- package/src/validation.ts +7 -4
- package/src/verify.test.ts +18 -2
- package/src/verify.ts +7 -3
|
@@ -1,12 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"type": "object",
|
|
4
|
-
"required": ["schema_version"
|
|
4
|
+
"required": ["schema_version"],
|
|
5
5
|
"properties": {
|
|
6
6
|
"schema_version": {"const": 1},
|
|
7
|
-
"
|
|
8
|
-
"packs": {"type": "array", "items": {"
|
|
9
|
-
"
|
|
7
|
+
"orly_version": {"type": "string", "pattern": "^$|^\\d+\\.\\d+\\.\\d+(?:-[a-zA-Z0-9.-]+)?(?:\\+[a-zA-Z0-9.-]+)?$"},
|
|
8
|
+
"packs": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}},
|
|
9
|
+
"managed": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}},
|
|
10
|
+
"digests": {
|
|
11
|
+
"type": "object", "propertyNames": {"$ref": "#/$defs/nonempty"},
|
|
12
|
+
"additionalProperties": {"type": "string", "pattern": "^(?:sha256:)?[a-f0-9]{64}$"}
|
|
13
|
+
},
|
|
14
|
+
"commands": {
|
|
15
|
+
"type": "object", "propertyNames": {"$ref": "#/$defs/nonempty"},
|
|
16
|
+
"additionalProperties": {
|
|
17
|
+
"type": "array", "minItems": 1,
|
|
18
|
+
"items": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/nonempty"}}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"surfaces": {
|
|
22
|
+
"type": "object", "additionalProperties": false,
|
|
23
|
+
"properties": {
|
|
24
|
+
"user": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}},
|
|
25
|
+
"docs": {"type": "array", "items": {"$ref": "#/$defs/nonempty"}}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"limits": {
|
|
29
|
+
"type": "object", "propertyNames": {"$ref": "#/$defs/nonempty"},
|
|
30
|
+
"additionalProperties": {
|
|
31
|
+
"type": "object", "additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"timeout_ms": {"type": "integer", "minimum": 1, "maximum": 2147478647},
|
|
34
|
+
"output_bytes": {"type": "integer", "minimum": 1, "maximum": 9007199254740991}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"execution": {
|
|
39
|
+
"type": "object", "required": ["remote"], "additionalProperties": false,
|
|
40
|
+
"properties": {
|
|
41
|
+
"remote": {
|
|
42
|
+
"type": "object", "required": ["mode"], "additionalProperties": false,
|
|
43
|
+
"properties": {
|
|
44
|
+
"mode": {"const": "disabled"},
|
|
45
|
+
"launcher": {"type": "array", "minItems": 1, "maxItems": 32, "items": {"allOf": [{"$ref": "#/$defs/nonempty"}, {"maxLength": 1024}]}}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
10
50
|
},
|
|
11
|
-
"additionalProperties": false
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"$defs": {
|
|
53
|
+
"nonempty": {"type": "string", "minLength": 1, "pattern": "^[^\\u0000]+$"}
|
|
54
|
+
}
|
|
12
55
|
}
|
|
@@ -15,7 +15,7 @@ At invocation start, record the consent-gated usage signal without blocking the
|
|
|
15
15
|
command -v orly >/dev/null 2>&1 && ORLY_INVOCATION=skill orly skill-event orly-babysit-prs >/dev/null 2>&1 || true
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Greptile reviews on GitHub and GitLab
|
|
19
19
|
post auto-reviews **asynchronously**. They land as PR review comments / MR
|
|
20
20
|
discussion threads, not check runs, so `gh pr checks --watch` (or the glab
|
|
21
21
|
equivalent) doesn't observe them. Without explicit polling on a backoff
|
|
@@ -42,8 +42,8 @@ Then walk it section by section:
|
|
|
42
42
|
| Triage section | What this skill triggers |
|
|
43
43
|
|---|---|
|
|
44
44
|
| `## Fetch` | Run per detected review thread (multi-thread loop below), via the forge layer |
|
|
45
|
-
| `## Suppressions Check` | Read
|
|
46
|
-
| `## Classify` | Read
|
|
45
|
+
| `## Suppressions Check` | Read history as context; a prior `fp` label never suppresses a current finding. Dismissal requires the owner's item-specific decision and recorded reason. |
|
|
46
|
+
| `## Classify` | Read the finding's code and evidence; classify actionable, already fixed or disputed. A disputed finding stays open for the owner. |
|
|
47
47
|
| `## Reply APIs` | Tier 1 first response, Tier 2 on re-flag — via the forge layer |
|
|
48
48
|
| `## Reply Templates` | Use Tier 1 / Tier 2 template verbatim — do not invent new wording |
|
|
49
49
|
| `## Severity Assessment & Re-ranking` | Re-rank the bot's severity against the project's actual risk profile |
|
|
@@ -74,9 +74,8 @@ echo "BABYSIT: forge=$FORGE origin=$ORIGIN"
|
|
|
74
74
|
| "walk every…" | every review id | every discussion thread id |
|
|
75
75
|
| Reply | reply to the review comment | post a note to the discussion |
|
|
76
76
|
|
|
77
|
-
Both forges
|
|
78
|
-
|
|
79
|
-
present).
|
|
77
|
+
Both forges filter bot authors by a case-insensitive `greptile` match.
|
|
78
|
+
Completion, review threads and summaries use that same selection.
|
|
80
79
|
|
|
81
80
|
## STEP 0.75 — CI checks must go green (poll + fix)
|
|
82
81
|
|
|
@@ -93,27 +92,42 @@ Each cycle, after the review-thread walk, poll CI and act by cause:
|
|
|
93
92
|
### GitHub
|
|
94
93
|
|
|
95
94
|
```bash
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
set -euo pipefail
|
|
96
|
+
CHECKS=$(gh pr checks "$PR_NUMBER" --json name,state,bucket,link) \
|
|
97
|
+
|| { echo "BABYSIT: check fetch incomplete; reset quiet polls" >&2; exit 1; }
|
|
98
|
+
printf '%s' "$CHECKS" | jq -e 'type == "array" and length > 0 and all(.[]; .bucket == "pass" or .bucket == "skipping")' >/dev/null \
|
|
99
|
+
|| { echo "BABYSIT: checks empty or not green; reset quiet polls" >&2; exit 1; }
|
|
100
|
+
CHECK_HEAD=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
|
|
101
|
+
[ "$CHECK_HEAD" = "$POLL_HEAD" ] \
|
|
102
|
+
|| { echo "BABYSIT: revision changed during checks; reset quiet polls" >&2; exit 1; }
|
|
103
|
+
echo "BABYSIT: ci=green revision=$CHECK_HEAD"
|
|
98
104
|
```
|
|
99
105
|
|
|
100
106
|
### GitLab
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
PIPELINES=$(glab api "projects/$PROJ_ENC/merge_requests/$MR_IID/pipelines" --paginate | jq -sc 'add') \
|
|
111
|
+
|| { echo "BABYSIT: pipeline fetch incomplete; reset quiet polls" >&2; exit 1; }
|
|
112
|
+
printf '%s' "$PIPELINES" | jq -e --arg revision "$POLL_HEAD" \
|
|
113
|
+
'[.[] | select(.sha == $revision)] | sort_by(.id) | last | .status == "success"' >/dev/null \
|
|
114
|
+
|| { echo "BABYSIT: current revision has no successful pipeline; reset quiet polls" >&2; exit 1; }
|
|
115
|
+
CHECK_HEAD=$(glab mr view -F json | jq -er '.sha // .diff_refs.head_sha')
|
|
116
|
+
[ "$CHECK_HEAD" = "$POLL_HEAD" ] \
|
|
117
|
+
|| { echo "BABYSIT: revision changed during check fetch; reset quiet polls" >&2; exit 1; }
|
|
118
|
+
printf 'ci=green revision=%s\n' "$CHECK_HEAD"
|
|
106
119
|
```
|
|
107
120
|
|
|
108
121
|
| CI state | Action |
|
|
109
122
|
|---|---|
|
|
110
|
-
| all `pass`/`skipping` (GitLab `success`) | Record `ci=green
|
|
123
|
+
| nonempty, completely fetched checks all `pass`/`skipping` (GitLab current-revision `success`) | Record `ci=green` for the unchanged pushed revision. |
|
|
124
|
+
| fetch failed, malformed response, or no checks found | Record `ci=incomplete`; reset quiet polls and retry. An empty response is never green. |
|
|
111
125
|
| any `pending`/`running` | Record `ci=pending`; re-poll on the cadence. **Never declare done while a check is still running.** |
|
|
112
126
|
| any `fail`/`cancel` **from your changes** | In-scope finding you OWN — treat like a greptile fix. Fetch the failing log (`gh run view <run-id> --log-failed`, run id from the check `link`; GitLab `glab ci trace <job-id>`), fix the code, re-verify with the relevant `make` target, commit, push, re-poll. |
|
|
113
127
|
| any `fail` **NOT from your changes** (pre-existing red, infra flake, unrelated job) | Surface to the user with job name + link. Do **not** fix blindly, do **not** edit CI config. Pause the done-declaration until they decide. |
|
|
114
128
|
|
|
115
129
|
CI failures caused by your commit are not "surface and wait" — they are the
|
|
116
|
-
same
|
|
130
|
+
same repair duty as a greptile P0: diagnose, fix the code (never the CI config),
|
|
117
131
|
push, and re-poll until green.
|
|
118
132
|
|
|
119
133
|
## Triggers
|
|
@@ -123,7 +137,7 @@ push, and re-poll until green.
|
|
|
123
137
|
- User says: "babysit", "watch greptile", "poll the PR", "poll the MR",
|
|
124
138
|
"watch reviews", "follow up on review feedback", "what did greptile say".
|
|
125
139
|
|
|
126
|
-
## Output
|
|
140
|
+
## Output rules
|
|
127
141
|
|
|
128
142
|
Per cycle, print one line (`PR` on GitHub, `MR` on GitLab):
|
|
129
143
|
|
|
@@ -152,17 +166,38 @@ via the agent's native cron/wakeup mechanism.
|
|
|
152
166
|
### GitHub (`FORGE=github`)
|
|
153
167
|
|
|
154
168
|
```bash
|
|
169
|
+
set -euo pipefail
|
|
155
170
|
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
|
|
156
171
|
PR_NUMBER=$(gh pr view --json number --jq '.number')
|
|
172
|
+
POLL_HEAD=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
|
|
173
|
+
[ "$POLL_HEAD" = "$(git rev-parse HEAD)" ] \
|
|
174
|
+
|| { echo "BABYSIT: local and pushed revisions differ; reset quiet polls" >&2; exit 1; }
|
|
175
|
+
|
|
176
|
+
REVIEWS=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" --paginate --slurp | jq -c 'add')
|
|
177
|
+
COMMENTS=$(gh api "repos/$REPO/pulls/$PR_NUMBER/comments" --paginate --slurp | jq -c 'add')
|
|
178
|
+
SUMMARIES=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate --slurp | jq -c 'add')
|
|
179
|
+
for payload in "$REVIEWS" "$COMMENTS" "$SUMMARIES"; do
|
|
180
|
+
printf '%s' "$payload" | jq -e 'type == "array" and all(.[]; (.id | type == "number") and (.user.login | type == "string"))' >/dev/null \
|
|
181
|
+
|| { echo "BABYSIT: malformed review response; reset quiet polls" >&2; exit 1; }
|
|
182
|
+
done
|
|
183
|
+
printf '%s' "$REVIEWS" | jq -e --arg revision "$POLL_HEAD" --arg bot "greptile" \
|
|
184
|
+
'any(.[]; (.user.login | test($bot; "i")) and .commit_id == $revision and (.state == "COMMENTED" or .state == "APPROVED" or .state == "CHANGES_REQUESTED"))' >/dev/null \
|
|
185
|
+
|| { echo "BABYSIT: current revision review pending; reset quiet polls" >&2; exit 1; }
|
|
186
|
+
END_HEAD=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
|
|
187
|
+
[ "$POLL_HEAD" = "$END_HEAD" ] \
|
|
188
|
+
|| { echo "BABYSIT: revision changed during review fetch; reset quiet polls" >&2; exit 1; }
|
|
189
|
+
jq -n --arg revision "$POLL_HEAD" --argjson reviews "$REVIEWS" \
|
|
190
|
+
--argjson comments "$COMMENTS" --argjson summaries "$SUMMARIES" \
|
|
191
|
+
'{revision:$revision,reviews:$reviews,comments:$comments,summaries:$summaries}'
|
|
157
192
|
|
|
158
193
|
# Walk EVERY review id — greptile may post more than one review per push.
|
|
159
|
-
REVIEW_IDS=$(
|
|
160
|
-
|
|
194
|
+
REVIEW_IDS=$(printf '%s' "$REVIEWS" | jq -r \
|
|
195
|
+
'.[] | select(.user.login | test("greptile"; "i")) | .id')
|
|
161
196
|
|
|
162
197
|
for rid in $REVIEW_IDS; do
|
|
163
198
|
# Follow greptile-triage.md (STEP 0): fetch line+top-level comments for THIS
|
|
164
199
|
# review id; Suppressions Check; Classify; Reply (Tier 1/Tier 2); history write.
|
|
165
|
-
# Fetch
|
|
200
|
+
# Fetch from the complete COMMENTS snapshot by pull_request_review_id.
|
|
166
201
|
# Reply: gh api -X POST "repos/$REPO/pulls/$PR_NUMBER/comments/<cid>/replies" -f body="..."
|
|
167
202
|
:
|
|
168
203
|
done
|
|
@@ -173,26 +208,32 @@ done
|
|
|
173
208
|
# (2) the top-level PR issue comment (greptile's "description reply" / summary,
|
|
174
209
|
# often with collapsible per-file sections listing concerns inline).
|
|
175
210
|
# Run each through greptile-triage.md exactly like a line comment.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
gh api "repos/$REPO/issues/$PR_NUMBER/comments" \
|
|
179
|
-
--jq '.[] | select(.user.login|test("greptile";"i")) | {id, body}'
|
|
211
|
+
printf '%s' "$REVIEWS" | jq -r '.[] | select(.user.login|test("greptile";"i")) | select(.body != "") | .body'
|
|
212
|
+
printf '%s' "$SUMMARIES" | jq -c '.[] | select(.user.login|test("greptile";"i")) | {id, body}'
|
|
180
213
|
# Reply to a summary issue comment: gh api -X POST "repos/$REPO/issues/$PR_NUMBER/comments" -f body="..."
|
|
181
214
|
```
|
|
182
215
|
|
|
183
216
|
### GitLab (`FORGE=gitlab`)
|
|
184
217
|
|
|
185
218
|
```bash
|
|
219
|
+
set -euo pipefail
|
|
186
220
|
# Project full path (e.g. gpu/console), URL-encoded for the API.
|
|
187
|
-
PROJECT_PATH=$(glab repo view -F json
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
221
|
+
PROJECT_PATH=$(glab repo view -F json | jq -er '.path_with_namespace | strings | select(length > 0)')
|
|
222
|
+
PROJ_ENC=$(printf '%s' "$PROJECT_PATH" | jq -sRr '@uri')
|
|
223
|
+
MR_IID=$(glab mr view -F json | jq -er '.iid | numbers')
|
|
224
|
+
POLL_HEAD=$(glab mr view -F json | jq -er '.sha // .diff_refs.head_sha')
|
|
225
|
+
[ "$POLL_HEAD" = "$(git rev-parse HEAD)" ] \
|
|
226
|
+
|| { echo "BABYSIT: local and pushed revisions differ; reset quiet polls" >&2; exit 1; }
|
|
191
227
|
|
|
192
228
|
# Walk EVERY discussion thread from the review bot. GitLab has no "review"
|
|
193
229
|
# object — a bot posts discussion threads (each with notes[]).
|
|
194
|
-
|
|
195
|
-
|
|
230
|
+
DISCUSSIONS=$(glab api "projects/$PROJ_ENC/merge_requests/$MR_IID/discussions" --paginate | jq -sc 'add')
|
|
231
|
+
NOTES=$(glab api "projects/$PROJ_ENC/merge_requests/$MR_IID/notes" --paginate | jq -sc 'add')
|
|
232
|
+
printf '%s' "$DISCUSSIONS" | jq -e 'type == "array" and all(.[]; (.id | type == "string") and (.notes | type == "array") and all(.notes[]; (.author.username | type == "string")))' >/dev/null \
|
|
233
|
+
|| { echo "BABYSIT: malformed discussion evidence; reset quiet polls" >&2; exit 1; }
|
|
234
|
+
printf '%s' "$NOTES" | jq -e 'type == "array" and all(.[]; (.id | type == "number") and (.author.username | type == "string"))' >/dev/null \
|
|
235
|
+
|| { echo "BABYSIT: malformed summary evidence; reset quiet polls" >&2; exit 1; }
|
|
236
|
+
THREAD_IDS=$(printf '%s' "$DISCUSSIONS" | jq -r '.[] | select(any(.notes[]; .author.username | test("greptile"; "i"))) | .id')
|
|
196
237
|
|
|
197
238
|
for tid in $THREAD_IDS; do
|
|
198
239
|
# Follow greptile-triage.md (STEP 0): fetch the thread's notes (body +
|
|
@@ -206,9 +247,13 @@ done
|
|
|
206
247
|
# ALSO triage greptile's SUMMARY feedback — the MR-level notes greptile posts
|
|
207
248
|
# outside a positioned discussion thread (the "description reply" / overview
|
|
208
249
|
# note, often with inline per-file concerns). Run each through greptile-triage.md.
|
|
209
|
-
|
|
210
|
-
--jq '.[] | select(.author.username|test("greptile";"i")) | select(.type == null) | {id, body}'
|
|
250
|
+
printf '%s' "$NOTES" | jq -c '.[] | select(.author.username|test("greptile";"i")) | select(.type == null) | {id, body}'
|
|
211
251
|
# Reply to a summary note: glab api -X POST "projects/$PROJ_ENC/merge_requests/$MR_IID/notes" -f body="..."
|
|
252
|
+
END_HEAD=$(glab mr view -F json | jq -er '.sha // .diff_refs.head_sha')
|
|
253
|
+
[ "$POLL_HEAD" = "$END_HEAD" ] \
|
|
254
|
+
|| { echo "BABYSIT: revision changed during review fetch; reset quiet polls" >&2; exit 1; }
|
|
255
|
+
jq -n --arg revision "$POLL_HEAD" --argjson discussions "$DISCUSSIONS" --argjson summaries "$NOTES" \
|
|
256
|
+
'{revision: $revision, discussions: $discussions, summaries: $summaries}'
|
|
212
257
|
```
|
|
213
258
|
|
|
214
259
|
### History paths (forge-independent)
|
|
@@ -232,16 +277,18 @@ Every triaged comment writes one line to BOTH:
|
|
|
232
277
|
|
|
233
278
|
| File | Purpose |
|
|
234
279
|
|---|---|
|
|
235
|
-
| `$HOME/.gstack/projects/<slug>/greptile-history.md` | Per-project
|
|
280
|
+
| `$HOME/.gstack/projects/<slug>/greptile-history.md` | Per-project context; no historical label automatically dismisses a finding. |
|
|
236
281
|
| `$HOME/.gstack/greptile-history.md` | Global aggregate. Cross-project retro / pattern-mining. |
|
|
237
282
|
|
|
238
283
|
Line format (from the triage helper's "History File Writes" section):
|
|
239
284
|
|
|
240
285
|
```
|
|
241
|
-
<YYYY-MM-DD> | <owner/repo> | <type:fp|fix|already-fixed> | <file-pattern> | <category>
|
|
286
|
+
<YYYY-MM-DD> | <owner/repo> | <type:fp|fix|already-fixed|disputed> | <file-pattern> | <category>
|
|
242
287
|
```
|
|
243
288
|
|
|
244
|
-
- **`fp`** —
|
|
289
|
+
- **`fp`** — owner-confirmed dismissal for a named finding, with its quote,
|
|
290
|
+
reason and reviewed revision recorded in Session Notes. Reassess on later revisions.
|
|
291
|
+
- **`disputed`** — awaiting the owner's decision; remains open and blocks done.
|
|
245
292
|
- **`fix`** — real issue, fixed in this cycle. Not suppressed.
|
|
246
293
|
- **`already-fixed`** — real issue, fixed in a prior commit on this branch. Not suppressed.
|
|
247
294
|
|
|
@@ -263,10 +310,16 @@ The categories are a fixed set: `race-condition`, `null-check`, `error-handling`
|
|
|
263
310
|
|
|
264
311
|
## Stop conditions
|
|
265
312
|
|
|
266
|
-
- **Done = two consecutive empty polls AND CI green.** Both must hold: no new
|
|
313
|
+
- **Done = two complete, consecutive empty polls AND CI green on the same pushed revision.** Both must hold: no new
|
|
267
314
|
reviews/threads/summary findings for two polls **and** STEP 0.75 reports
|
|
268
315
|
`ci=green`. A quiet bot with a still-`pending` or red CI is NOT done — keep
|
|
269
316
|
polling on the cadence.
|
|
317
|
+
- Every cycle fetches all pages of reviews, line comments and summaries.
|
|
318
|
+
Fetch failure, missing checks, a disputed finding or a changed revision resets
|
|
319
|
+
the quiet counter; compare the remote revision again after check collection.
|
|
320
|
+
- Missing review completion for the current pushed revision is `review=pending`,
|
|
321
|
+
even when all fetches succeed. Surface it after the recorded retry cadence;
|
|
322
|
+
two empty responses alone cannot prove the reviewer completed its work.
|
|
270
323
|
- **PR/MR merged or closed** → stop, report.
|
|
271
324
|
- **CI red FROM your changes** → fix it (STEP 0.75): diagnose, fix the code,
|
|
272
325
|
push, re-poll. Not a stop condition — it's work to do.
|
|
@@ -320,7 +373,7 @@ BABYSIT REPORT — <PR|MR> #<n> @ <SHA>
|
|
|
320
373
|
- `docs/greptile-learnings/RULES.md` — project-level named rules
|
|
321
374
|
(durable principles). New rule added here only when the finding
|
|
322
375
|
generalizes; per-incident rows go to greptile-history.md.
|
|
323
|
-
- this repository's `AGENTS.md` /
|
|
376
|
+
- this repository's `AGENTS.md` / `.orly/AGENTS.md` — CHORE(close) skill
|
|
324
377
|
chain step 4 cites this skill.
|
|
325
378
|
- `gh api` docs: https://cli.github.com/manual/gh_api
|
|
326
379
|
- `glab api` docs: https://gitlab.com/gitlab-org/cli (`glab api --help`)
|
|
@@ -60,6 +60,13 @@ Determinism starts here. Before copying the template:
|
|
|
60
60
|
|
|
61
61
|
## Step 2 — Lock review-readiness (so the PR ships clean)
|
|
62
62
|
|
|
63
|
+
**Optional Jev advice:** After capturing one concrete requirement, prepare a
|
|
64
|
+
`plan` manifest for `plan.prerequisites` or `plan.observable_result`.
|
|
65
|
+
Use `orly judge plan --input <manifest>` for exact offline replay. Add
|
|
66
|
+
`--refresh` only when selected source upload is authorized; use
|
|
67
|
+
`orly judge --help` for the manifest and limits. Inspect uncertain or adverse answers before accepting
|
|
68
|
+
the plan. Advice never replaces this skill's checklist or owner decisions.
|
|
69
|
+
|
|
63
70
|
This is the step that prevents greptile findings — the spec becomes a pre-commitment to the rules its code must obey:
|
|
64
71
|
|
|
65
72
|
- **Applicable Rules** — name the *specific* `docs/greptile-learnings/RULES.md` rule IDs the diff will trip (e.g. NDC, NLR, NLG, UFS), plus the per-surface dispatch façades / rule files: `dispatch/write_zig.md` (`*.zig`), `docs/REST_API_DESIGN_GUIDELINES.md` (`src/http/handlers/**`), `docs/SCHEMA_CONVENTIONS.md` (`schema/*`), `dispatch/write_ts_adhere_bun.md`, `docs/LOGGING_STANDARD.md`, `docs/LIFECYCLE_PATTERNS.md`. Generic "follow RULES.md" earns a greptile finding; named IDs the implementer obeys by construction do not. Cite rule docs by their repo-local `docs/…` paths — the selected packs materialise them into every repository; cite only files that exist here (`test -f` each).
|
|
@@ -88,7 +95,8 @@ Now the file. Pick the ID and copy the template:
|
|
|
88
95
|
# next free M{N}_{WS}
|
|
89
96
|
ls docs/v*/pending/ docs/v*/active/ docs/v*/done/ 2>/dev/null \
|
|
90
97
|
| grep -oE 'M[0-9]+_[0-9]+' | sort -u | tail -5
|
|
91
|
-
|
|
98
|
+
sed -n '/^# Spec Body — Copy Everything Below This Line/,$p' docs/TEMPLATE.md \
|
|
99
|
+
| sed '1d' > docs/v{N}/pending/M{N}_{WS}_P{P}_{CATEGORIES}_{NAME}.md
|
|
92
100
|
```
|
|
93
101
|
|
|
94
102
|
|
|
@@ -125,17 +133,22 @@ Before the spec leaves `pending/`, it must pass this checklist — the determini
|
|
|
125
133
|
- [ ] reporting sections present (Discovery; Acceptance Rubric with Graded column empty)
|
|
126
134
|
- [ ] `bash audits/spec-template.sh --staged` is clean (it BLOCKs missing required sections and unfilled `{placeholders}`)
|
|
127
135
|
|
|
128
|
-
|
|
136
|
+
Commit a new milestone spec on the repository's default branch, as the
|
|
137
|
+
operating model requires. Check the remote and branch before authoring;
|
|
138
|
+
do not infer the default branch name.
|
|
129
139
|
|
|
130
|
-
-
|
|
131
|
-
-
|
|
140
|
+
- Inside an active stream, revise its existing spec on the active branch.
|
|
141
|
+
- A new, separate milestone inside an active stream needs the owner's decision
|
|
142
|
+
before creating another worktree or changing branches.
|
|
143
|
+
- Never move the active stream's uncommitted work to the default branch.
|
|
132
144
|
|
|
133
145
|
```bash
|
|
134
146
|
git add docs/v{N}/pending/M{N}_{WS}_*_{NAME}.md
|
|
135
147
|
git commit -m "docs(m{N}): add spec — {short title}"
|
|
136
148
|
```
|
|
137
149
|
|
|
138
|
-
The spec lands in `pending/` on the branch
|
|
150
|
+
The new spec lands in `pending/` on the default branch.
|
|
151
|
+
CHORE(open) moves it to `active/` and creates the implementation worktree.
|
|
139
152
|
|
|
140
153
|
---
|
|
141
154
|
|
|
@@ -143,7 +156,7 @@ The spec lands in `pending/` on the branch/worktree where the skill was invoked.
|
|
|
143
156
|
|
|
144
157
|
- It does **not** start coding, create a worktree, or move the spec to `active/`. That's CHORE(open), when implementation begins.
|
|
145
158
|
- It does **not** modify any rule file, ARCHITECTURE doc, or changelog.
|
|
146
|
-
- It does
|
|
159
|
+
- It does not create an implementation branch or silently switch an active stream.
|
|
147
160
|
|
|
148
161
|
## Failure modes
|
|
149
162
|
|
|
@@ -68,7 +68,7 @@ Spec asserts "503 on Redis down", code returns 200 → test the spec, flag the c
|
|
|
68
68
|
|
|
69
69
|
1. **Read the spec** — Failure Modes, Error Contracts, Concurrency Contracts, Resource Limits, Streaming Contracts tables are authoritative.
|
|
70
70
|
2. **Read `docs/greptile-learnings/RULES.md`** — every rule maps to a regression test.
|
|
71
|
-
3. **Bring up real deps** —
|
|
71
|
+
3. **Bring up real deps** — use the repository's declared local dependency startup command. Verify health before writing tests; do not add a datastore the application does not use.
|
|
72
72
|
4. **Follow lifecycle timing** — record the comparison revision at opening; measure full unit and integration baselines before the Pull Request. Run focused reproduction and Section proofs while implementing; first application hydration occurs at Section verification. `dispatch/lifecycle.md` owns the sequence.
|
|
73
73
|
5. **Map the ordered request path** — list every dependency interaction in execution order, including repeated acquisitions of the same resource, plus every `catch`/`orelse`/`except`/`Err` in the chain. That list seeds T4 and its partial-completion matrix.
|
|
74
74
|
|
|
@@ -80,7 +80,7 @@ Spec asserts "503 on Redis down", code returns 200 → test the spec, flag the c
|
|
|
80
80
|
| **Standard** | New service method, new Redis stream/key, schema change with logic | + T4 + T5 + T6 |
|
|
81
81
|
| **Hardening** | Auth / payment / lease / migration / streaming / anything in the data-loss radius | + T7 (if applicable) + T8 + chaos pass |
|
|
82
82
|
|
|
83
|
-
Auto-detect from
|
|
83
|
+
Auto-detect from the repository's actual authentication, lease/reclaim/fence and client-daemon surfaces, schema migrations and streaming handlers → Hardening. The client-daemon surface also requires T9. CRUD-only against existing schema → Smoke. Default → Standard.
|
|
84
84
|
|
|
85
85
|
Any operation that touches multiple systems or acquires the same resource more than once is **Standard at minimum** and must run T4, even when the endpoint is otherwise CRUD-only.
|
|
86
86
|
|
|
@@ -201,7 +201,7 @@ idempotency, compensation, reconciliation, and failure tests for each residual s
|
|
|
201
201
|
**Parallelism at scale (≥100 connections) — proves there's no hidden global lock.**
|
|
202
202
|
A single global `Thread.Mutex` keeps every correctness test green while serialising
|
|
203
203
|
every request; this is the regression that proof exists to catch.
|
|
204
|
-
- **≥100 concurrent connections** against
|
|
204
|
+
- **≥100 concurrent connections** against the application's real dependencies, released together off a
|
|
205
205
|
**barrier** so contention is real and reproducible (not staggered by spawn latency).
|
|
206
206
|
- **Correctness invariant (deterministic hard gate):** same row / idempotency key / lease
|
|
207
207
|
under the 100-way race → **exactly one effect**, no lost update.
|
|
@@ -393,7 +393,7 @@ Cross-stack failure-injection tools:
|
|
|
393
393
|
|
|
394
394
|
Stack-specific must-knows:
|
|
395
395
|
|
|
396
|
-
- **Zig** —
|
|
396
|
+
- **Zig** — drain every query result before releasing its connection, including early exits. Use wrapper-owned draining where the repository provides it. Review the owner's cleanup path and run any actually declared cleanup check. Use a per-request arena for request ownership. Exercise `std.testing.allocator` over the full request lifecycle to catch cross-thread leaks. If integration tests share the binary with unit tests, assert the live-dependency setting in Continuous Integration (CI). Cross-compile the repository's declared platform matrix before declaring done.
|
|
397
397
|
- **Python** — `pytest-asyncio` event loop scope = `function`, not `session` (avoid leakage). `respx`/`responses` for external HTTP only.
|
|
398
398
|
- **Rust** — `#[sqlx::test]` for per-test schema; `tokio::test(flavor = "multi_thread")` for real concurrency. `loom` for races, not raw threads.
|
|
399
399
|
- **Node** — `--isolate` per test if framework supports; otherwise reset module registry.
|
|
@@ -83,6 +83,15 @@ codebase, and it's the artifact that makes "is the skill applied?" auditable.
|
|
|
83
83
|
|
|
84
84
|
## Three execution modes
|
|
85
85
|
|
|
86
|
+
**Optional Jev advice:** Before accepting a linked test as proof, describe its
|
|
87
|
+
required behavior in a `verify.assertion` manifest with complete implementation
|
|
88
|
+
and test evidence. Use `orly judge verify --input <manifest>` for exact replay;
|
|
89
|
+
`--refresh` explicitly permits scanned source upload. Use `orly judge --help`
|
|
90
|
+
for the manifest and limits.
|
|
91
|
+
A weak assertion answer suggests an exact-result assertion. Run that assertion
|
|
92
|
+
against correct and controlled incorrect behavior. Jev advice never supplies
|
|
93
|
+
test results, waives missing coverage or changes a deterministic gate.
|
|
94
|
+
|
|
86
95
|
Pick one. Don't run all categories on every file.
|
|
87
96
|
|
|
88
97
|
| Mode | When | Required categories |
|
|
@@ -275,7 +284,7 @@ When a spec exists with these tables, treat them as authoritative test sources:
|
|
|
275
284
|
|
|
276
285
|
| Spec table | Generates |
|
|
277
286
|
|---|---|
|
|
278
|
-
| Test Specification | One test per row
|
|
287
|
+
| Test Specification | One behavior-named test per row; the spec maps Dimension numbers to test names. Keep planning identifiers out of source test names and filenames. |
|
|
279
288
|
| Error Contracts | One negative test per row asserting exact error |
|
|
280
289
|
| Failure Modes | One integration test per row with deterministic injection |
|
|
281
290
|
| Partial Completion | One row per ordered failure point with workflow-ordinal and boundary-call-ordinal injection plus residual-state assertions |
|
package/src/cli.test.ts
CHANGED
|
@@ -32,7 +32,7 @@ describe("orly command", () => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
// Adversarial review found recordOptIn running before the --dry-run branch,
|
|
35
|
-
// so a preview created .
|
|
35
|
+
// so a preview created .orly/orly.json — and on an installed repository
|
|
36
36
|
// permanently added the pack. A dry run changes nothing, opt-in included.
|
|
37
37
|
test("--dry-run records no pack", () => {
|
|
38
38
|
const repo = mkdtempSync(join(tmpdir(), "orly-dry-"));
|
|
@@ -44,7 +44,7 @@ describe("orly command", () => {
|
|
|
44
44
|
|
|
45
45
|
expect(result.exitCode).toBe(0);
|
|
46
46
|
expect(result.stdout.toString()).toContain("Generated by");
|
|
47
|
-
expect(existsSync(join(repo, ".
|
|
47
|
+
expect(existsSync(join(repo, ".orly/orly.json"))).toBe(false);
|
|
48
48
|
expect(existsSync(join(repo, "AGENTS.orly.md"))).toBe(false);
|
|
49
49
|
} finally {
|
|
50
50
|
rmSync(repo, { recursive: true, force: true });
|
package/src/cli.ts
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { printHelp } from "./cli_help";
|
|
6
|
+
import { selectPacks } from "./config_discovery";
|
|
7
|
+
import { CONFIG_PATH, managedDrift, readConfig, RepoConfig, staleVersion, writeConfig } from "./config";
|
|
6
8
|
import { GateReport, isGateName, recordOverride, runGate, runGates } from "./gates";
|
|
9
|
+
import { readInstallConfig } from "./installation/migration";
|
|
7
10
|
import { install, InstallResult } from "./install";
|
|
8
11
|
import { isString, OrlyError, readJsonObject, RulesModel } from "./model";
|
|
9
12
|
import { Renderer } from "./render";
|
|
@@ -33,9 +36,6 @@ const PR_GATE = "pr";
|
|
|
33
36
|
const ENGINE_MARKER = "evals/install/run.sh";
|
|
34
37
|
const VERIFY_COMMAND = "verify";
|
|
35
38
|
const SKILL_EVENT_COMMAND = "skill-event";
|
|
36
|
-
const TELEMETRY_CONFIG_KEY = "telemetry";
|
|
37
|
-
const ANONYMOUS_TELEMETRY_CONFIG = JSON.stringify({ [TELEMETRY_CONFIG_KEY]: "anonymous" });
|
|
38
|
-
const OFF_TELEMETRY_CONFIG = JSON.stringify({ [TELEMETRY_CONFIG_KEY]: "off" });
|
|
39
39
|
|
|
40
40
|
const { root, arguments: commandArguments } = parseRoot(Bun.argv.slice(2));
|
|
41
41
|
const startedAt = performance.now();
|
|
@@ -75,6 +75,16 @@ async function run(model: RulesModel, args: string[]): Promise<CliResult> {
|
|
|
75
75
|
if (command === "init") return cliResult(await materialise(model, rest, true));
|
|
76
76
|
if (command === "update") return cliResult(await materialise(model, rest, false));
|
|
77
77
|
if (command === VERIFY_COMMAND) return cliResult(await verify(model, rest));
|
|
78
|
+
if (command === "judge") {
|
|
79
|
+
const { judgmentCommand } = await import("./judgments/command");
|
|
80
|
+
return cliResult(await judgmentCommand(rest));
|
|
81
|
+
}
|
|
82
|
+
if (command === "lifecycle") {
|
|
83
|
+
if (rest.some((argument) => argument !== JSON_FLAG)) throw new OrlyError("usage: orly lifecycle [--json]");
|
|
84
|
+
const { lifecyclePlan } = await import("./execution/plan");
|
|
85
|
+
console.log(JSON.stringify(lifecyclePlan(projectRoot()), undefined, JSON_INDENT));
|
|
86
|
+
return cliResult(0);
|
|
87
|
+
}
|
|
78
88
|
if (command === "gate") return gate(model, rest);
|
|
79
89
|
if (command === "override") return cliResult(override(rest));
|
|
80
90
|
if (command === SKILL_EVENT_COMMAND) {
|
|
@@ -131,12 +141,12 @@ async function packageVersion(model: RulesModel): Promise<string> {
|
|
|
131
141
|
return manifest.version;
|
|
132
142
|
}
|
|
133
143
|
|
|
134
|
-
// Both verbs materialise the same way: the repository's own `.
|
|
144
|
+
// Both verbs materialise the same way: the repository's own `.orly/orly.json`
|
|
135
145
|
// names its packs and commands, so neither asks the caller who this repo is.
|
|
136
146
|
async function materialise(model: RulesModel, args: string[], isInit: boolean): Promise<number> {
|
|
137
147
|
model.validate();
|
|
138
148
|
const targetRoot = projectRoot();
|
|
139
|
-
const existing = await
|
|
149
|
+
const existing = await readInstallConfig(targetRoot);
|
|
140
150
|
if (!isInit && !existing) throw new OrlyError(`no ${CONFIG_PATH} here — run \`orly init\` first`);
|
|
141
151
|
const requested = optionalValues(args, WITH_FLAG);
|
|
142
152
|
// --dry-run changes nothing, and recording an opt-in pack is a change. It
|
|
@@ -146,9 +156,9 @@ async function materialise(model: RulesModel, args: string[], isInit: boolean):
|
|
|
146
156
|
// Otherwise the pack is a property of the repository, recorded before the
|
|
147
157
|
// render rather than passed through it: the next `orly update` in a
|
|
148
158
|
// teammate's clone selects the same set with no flag to remember.
|
|
149
|
-
await recordOptIn(model, targetRoot, existing, requested);
|
|
150
159
|
const result = await install(model, {
|
|
151
160
|
targetRoot,
|
|
161
|
+
requestedPacks: requested,
|
|
152
162
|
force: args.includes(FORCE_FLAG),
|
|
153
163
|
installHooks: !args.includes(NO_HOOKS_FLAG),
|
|
154
164
|
orlyVersion: await packageVersion(model),
|
|
@@ -210,7 +220,8 @@ async function doctorInstall(model: RulesModel): Promise<string[]> {
|
|
|
210
220
|
// What init would write, without writing it. Replaces the `render` verb: the
|
|
211
221
|
// preview belongs on the command you are about to run, not beside it.
|
|
212
222
|
async function preview(model: RulesModel, targetRoot: string, requested: string[] = []): Promise<number> {
|
|
213
|
-
const
|
|
223
|
+
const config = await readInstallConfig(targetRoot);
|
|
224
|
+
const local = { packs: selectPacks(model, targetRoot, config?.packs ?? [], new Set(config?.managed ?? [])), commands: config?.commands ?? {} };
|
|
214
225
|
// selectPacks validates the requested names and unions them with what the
|
|
215
226
|
// repository's own sources select — the same call the real run makes, minus
|
|
216
227
|
// the write.
|
|
@@ -220,20 +231,6 @@ async function preview(model: RulesModel, targetRoot: string, requested: string[
|
|
|
220
231
|
return 0;
|
|
221
232
|
}
|
|
222
233
|
|
|
223
|
-
// Opt-in packs never auto-select from file extensions, so naming one is the
|
|
224
|
-
// only way it lands. Validated against the registry here, before anything is
|
|
225
|
-
// recorded, so a typo names the available set instead of writing a config the
|
|
226
|
-
// next command rejects.
|
|
227
|
-
async function recordOptIn(model: RulesModel, targetRoot: string, existing: RepoConfig | undefined, requested: string[]): Promise<void> {
|
|
228
|
-
if (requested.length === 0) return;
|
|
229
|
-
const config = existing ?? await seedConfig(targetRoot);
|
|
230
|
-
const packs = new Set(config.packs);
|
|
231
|
-
for (const name of requested) packs.add(name);
|
|
232
|
-
// selectPacks throws OrlyError on an unknown name, listing what is available.
|
|
233
|
-
selectPacks(model, targetRoot, [...packs]);
|
|
234
|
-
await writeConfig(targetRoot, { ...config, packs: [...packs].sort() });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
234
|
async function verify(model: RulesModel, args: string[]): Promise<number> {
|
|
238
235
|
requireEngineCheckout(model, VERIFY_COMMAND);
|
|
239
236
|
const checks = await verifyRenders(model);
|
|
@@ -290,56 +287,3 @@ function optionalValues(args: string[], name: string): string[] {
|
|
|
290
287
|
function cliResult(exitCode: number): CliResult {
|
|
291
288
|
return { exitCode };
|
|
292
289
|
}
|
|
293
|
-
|
|
294
|
-
function printHelp(): void {
|
|
295
|
-
console.log(`orly — prove the boundary; carry the rules
|
|
296
|
-
|
|
297
|
-
Gates (read-only; no PR without every criterion green or a recorded override):
|
|
298
|
-
orly gate [--accept-dirty] run work → verify → pr; stop at first red
|
|
299
|
-
orly gate <work|verify|pr> run one gate
|
|
300
|
-
work does this commit conform? the declared conform command; no git
|
|
301
|
-
state, so a commit hook's own dirty tree never blocks it
|
|
302
|
-
verify does the work hold up? configuration, docs language, and the
|
|
303
|
-
non-test verify.* set; unfinished Sections may be pushed
|
|
304
|
-
pr can this ship? branch, tree, pushed, every spec criterion, and
|
|
305
|
-
every declared verify.* command
|
|
306
|
-
orly override <CRITERION> --reason <REASON>
|
|
307
|
-
empty commit with an Orly-Override trailer
|
|
308
|
-
|
|
309
|
-
Install (the repository is the unit — no checkout of this package required):
|
|
310
|
-
orly init [--force] [--no-hooks] [--with <PACK>] [--dry-run] [--json]
|
|
311
|
-
materialise rules, gates, hooks, and a
|
|
312
|
-
seeded .oracle/orly.json. A repository that
|
|
313
|
-
already has an AGENTS.md keeps it: orly's
|
|
314
|
-
rules land as AGENTS.orly.md, reached by a
|
|
315
|
-
pointer block in the file you own. One
|
|
316
|
-
import line lands per runtime that needs
|
|
317
|
-
one (CLAUDE.md, opencode.json), so the
|
|
318
|
-
rules load rather than wait to be noticed;
|
|
319
|
-
a loader you wrote yourself is left alone.
|
|
320
|
-
orly update [--force] [--with <PACK>] [--dry-run] [--json]
|
|
321
|
-
re-materialise at the installed engine version
|
|
322
|
-
|
|
323
|
-
--with <PACK> record an opt-in pack (repeatable) in
|
|
324
|
-
.oracle/orly.json, so every clone selects it
|
|
325
|
-
--dry-run show what would be written; change nothing
|
|
326
|
-
|
|
327
|
-
orly doctor check this repository's installed rules
|
|
328
|
-
against what orly would write today
|
|
329
|
-
orly --version the installed package version
|
|
330
|
-
|
|
331
|
-
Usage telemetry (off by default):
|
|
332
|
-
Consent file: ~/.config/agentsfleet/orly/.orly.json
|
|
333
|
-
State-root override: AGENTSFLEET_STATE_DIR
|
|
334
|
-
Set ${ANONYMOUS_TELEMETRY_CONFIG} to send command, gate, and packaged Orly skill
|
|
335
|
-
names, outcome, failed criterion, duration, Orly version, operating system, architecture,
|
|
336
|
-
invocation type, timestamps, and random event, session, and installation IDs.
|
|
337
|
-
Set ${OFF_TELEMETRY_CONFIG} to write and send nothing.
|
|
338
|
-
ORLY_TELEMETRY=off|anonymous changes one process; ORLY_TELEMETRY_OFF=1 wins.
|
|
339
|
-
Orly never collects source, paths, repositories, branches, arguments, prompts,
|
|
340
|
-
output, environment values, raw errors, or personal and account details.
|
|
341
|
-
|
|
342
|
-
Ruleset authoring (an orly checkout only — refused from an installed package):
|
|
343
|
-
orly verify the rules render the same twice, and the
|
|
344
|
-
committed copy matches that render`);
|
|
345
|
-
}
|