agentforge-framework 0.2.0__py3-none-any.whl
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.
- agentforge_framework/.claude-plugin/plugin.json +4 -0
- agentforge_framework/__init__.py +3 -0
- agentforge_framework/agents/__init__.py +92 -0
- agentforge_framework/agents/architect.py +146 -0
- agentforge_framework/agents/implementer.py +162 -0
- agentforge_framework/agents/orchestrator.py +588 -0
- agentforge_framework/agents/reviewer.py +335 -0
- agentforge_framework/agents/security.py +138 -0
- agentforge_framework/agents/tester.py +125 -0
- agentforge_framework/cli.py +461 -0
- agentforge_framework/context/__init__.py +1 -0
- agentforge_framework/context/extractors/__init__.py +76 -0
- agentforge_framework/context/extractors/base.py +47 -0
- agentforge_framework/context/extractors/python.py +65 -0
- agentforge_framework/context/extractors/sql.py +121 -0
- agentforge_framework/context/extractors/yaml.py +59 -0
- agentforge_framework/context/prompt.py +104 -0
- agentforge_framework/context/resolver.py +185 -0
- agentforge_framework/core/__init__.py +1 -0
- agentforge_framework/core/commands.py +170 -0
- agentforge_framework/core/config.py +90 -0
- agentforge_framework/core/contracts.py +875 -0
- agentforge_framework/core/gates.py +333 -0
- agentforge_framework/core/issues.py +697 -0
- agentforge_framework/core/plan_format.py +272 -0
- agentforge_framework/core/process.py +141 -0
- agentforge_framework/core/project.py +262 -0
- agentforge_framework/core/registry.py +455 -0
- agentforge_framework/core/repo.py +185 -0
- agentforge_framework/core/router.py +1 -0
- agentforge_framework/core/runtime.py +639 -0
- agentforge_framework/core/skills.py +255 -0
- agentforge_framework/core/workflow.py +215 -0
- agentforge_framework/plugins/__init__.py +35 -0
- agentforge_framework/plugins/databricks/__init__.py +86 -0
- agentforge_framework/plugins/pyspark/__init__.py +57 -0
- agentforge_framework/plugins/python/__init__.py +45 -0
- agentforge_framework/plugins/sql/__init__.py +377 -0
- agentforge_framework/providers/__init__.py +48 -0
- agentforge_framework/providers/base.py +248 -0
- agentforge_framework/providers/claude.py +159 -0
- agentforge_framework/providers/codex.py +139 -0
- agentforge_framework/skills/MANIFEST.yaml +157 -0
- agentforge_framework/skills/NOTICE +49 -0
- agentforge_framework/skills/domain-modeling/ADR-FORMAT.md +47 -0
- agentforge_framework/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- agentforge_framework/skills/domain-modeling/SKILL.md +74 -0
- agentforge_framework/skills/domain-modeling/agents/openai.yaml +3 -0
- agentforge_framework/skills/grill-with-docs/SKILL.md +76 -0
- agentforge_framework/skills/grilling/SKILL.md +28 -0
- agentforge_framework/skills/grilling/agents/openai.yaml +3 -0
- agentforge_framework/skills/to-spec/SKILL.md +75 -0
- agentforge_framework/skills/to-spec/agents/openai.yaml +5 -0
- agentforge_framework/skills/to-tickets/SKILL.md +105 -0
- agentforge_framework/skills/to-tickets/agents/openai.yaml +5 -0
- agentforge_framework/skills/unslop/SKILL.md +131 -0
- agentforge_framework/skills/unslop/evals/fixtures/silhouette/human_reference.json +66 -0
- agentforge_framework/skills/unslop/scripts/_lang.py +106 -0
- agentforge_framework/skills/unslop/scripts/banned_phrase_scan.py +784 -0
- agentforge_framework/skills/unslop/scripts/calibrate_pairs.py +580 -0
- agentforge_framework/skills/unslop/scripts/calibrate_score.py +273 -0
- agentforge_framework/skills/unslop/scripts/check_packs.py +80 -0
- agentforge_framework/skills/unslop/scripts/check_suggestions.py +225 -0
- agentforge_framework/skills/unslop/scripts/contribute.py +373 -0
- agentforge_framework/skills/unslop/scripts/diff_check.py +139 -0
- agentforge_framework/skills/unslop/scripts/extract_constraints.py +201 -0
- agentforge_framework/skills/unslop/scripts/harvest_classify.py +223 -0
- agentforge_framework/skills/unslop/scripts/harvest_samples.py +534 -0
- agentforge_framework/skills/unslop/scripts/readability_metrics.py +295 -0
- agentforge_framework/skills/unslop/scripts/refresh_status.py +154 -0
- agentforge_framework/skills/unslop/scripts/silhouette_scan.py +390 -0
- agentforge_framework/skills/unslop/scripts/structure_scan.py +322 -0
- agentforge_framework/skills/unslop/scripts/suggest.py +211 -0
- agentforge_framework/skills/unslop/scripts/validate_preservation.py +409 -0
- agentforge_framework/skills/unslop/scripts/voice_card.py +496 -0
- agentforge_framework/skills/unslop/scripts/voice_profile.py +194 -0
- agentforge_framework/skills/unslop/scripts/voice_score.py +271 -0
- agentforge_framework/skills/unslop/scripts/wiki_sync.py +479 -0
- agentforge_framework/skills/write-plainly/SKILL.md +94 -0
- agentforge_framework/workflows/bugfix.yaml +8 -0
- agentforge_framework/workflows/feature.yaml +16 -0
- agentforge_framework/workflows/review.yaml +10 -0
- agentforge_framework-0.2.0.dist-info/METADATA +321 -0
- agentforge_framework-0.2.0.dist-info/RECORD +89 -0
- agentforge_framework-0.2.0.dist-info/WHEEL +5 -0
- agentforge_framework-0.2.0.dist-info/entry_points.txt +3 -0
- agentforge_framework-0.2.0.dist-info/licenses/LICENSE +202 -0
- agentforge_framework-0.2.0.dist-info/licenses/src/agentforge_framework/skills/NOTICE +49 -0
- agentforge_framework-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: to-tickets
|
|
3
|
+
description: Break a plan, spec, or the current conversation into a set of tracer-bullet tickets, each declaring its blocking edges, published to the configured tracker (edges as text in one file per ticket locally, or native blocking links on a real tracker).
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# To Tickets
|
|
8
|
+
|
|
9
|
+
Break a plan, spec, or conversation into a set of **tickets**: tracer-bullet vertical slices, each declaring the tickets that **block** it.
|
|
10
|
+
|
|
11
|
+
The issue tracker and triage label vocabulary should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
### 1. Gather context
|
|
16
|
+
|
|
17
|
+
Work from whatever is already in the conversation context. If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.
|
|
18
|
+
|
|
19
|
+
### 2. Explore the codebase (optional)
|
|
20
|
+
|
|
21
|
+
If you have not already explored the codebase, do so to understand the current state of the code. Ticket titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
|
|
22
|
+
|
|
23
|
+
Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
|
|
24
|
+
|
|
25
|
+
### 3. Draft vertical slices
|
|
26
|
+
|
|
27
|
+
Break the work into **tracer bullet** tickets.
|
|
28
|
+
|
|
29
|
+
<vertical-slice-rules>
|
|
30
|
+
|
|
31
|
+
- Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests): vertical, NOT a horizontal slice of one layer
|
|
32
|
+
- A completed slice is demoable or verifiable on its own
|
|
33
|
+
- Each slice is sized to fit in a single fresh context window
|
|
34
|
+
- Any prefactoring should be done first
|
|
35
|
+
|
|
36
|
+
</vertical-slice-rules>
|
|
37
|
+
|
|
38
|
+
Give each ticket its **blocking edges**: the other tickets that must complete before it can start. A ticket with no blockers can start immediately.
|
|
39
|
+
|
|
40
|
+
**Wide refactors are the exception to vertical slicing.** A **wide refactor** is one mechanical change (rename a column, retype a shared symbol) whose **blast radius** fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green. Don't force it into a tracer bullet; sequence it as **expand–contract**. First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by blast radius (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains, in a ticket blocked by every migrate batch. When even the batches can't stay green alone, keep the sequence but let them share an integration branch that all block a final integrate-and-verify ticket; green is promised only there.
|
|
41
|
+
|
|
42
|
+
### 4. Quiz the user
|
|
43
|
+
|
|
44
|
+
Present the proposed breakdown as a numbered list. For each ticket, show:
|
|
45
|
+
|
|
46
|
+
- **Title**: short descriptive name
|
|
47
|
+
- **Blocked by**: which other tickets (if any) must complete first
|
|
48
|
+
- **What it delivers**: the end-to-end behaviour this ticket makes work
|
|
49
|
+
|
|
50
|
+
Ask the user:
|
|
51
|
+
|
|
52
|
+
- Does the granularity feel right? (too coarse / too fine)
|
|
53
|
+
- Are the blocking edges correct: does each ticket only depend on tickets that genuinely gate it?
|
|
54
|
+
- Should any tickets be merged or split further?
|
|
55
|
+
|
|
56
|
+
Iterate until the user approves the breakdown.
|
|
57
|
+
|
|
58
|
+
### 5. Publish the tickets to the configured tracker
|
|
59
|
+
|
|
60
|
+
Publish the approved tickets. **How** depends on the tracker `/setup-matt-pocock-skills` configured; the tickets are the same either way, only the shape of the blocking edges changes:
|
|
61
|
+
|
|
62
|
+
- **Local files** → write one file per ticket under `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01` in dependency order (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below: one ticket per file, never a single combined file.
|
|
63
|
+
- **A real issue tracker (GitHub, Linear, …)** → publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the `ready-for-agent` triage label unless instructed otherwise; the tickets are agent-grabbable by construction.
|
|
64
|
+
|
|
65
|
+
Work the **frontier**: any ticket whose blockers are all done. For a purely linear chain that means top to bottom.
|
|
66
|
+
|
|
67
|
+
Do NOT close or modify any parent issue.
|
|
68
|
+
|
|
69
|
+
<local-ticket-template>
|
|
70
|
+
|
|
71
|
+
# <NN>: <Ticket title>
|
|
72
|
+
|
|
73
|
+
**What to build:** the end-to-end behaviour this ticket makes work, from the user's perspective, not a layer-by-layer implementation list.
|
|
74
|
+
|
|
75
|
+
**Blocked by:** the numbers/titles of the tickets that gate this one, or "None (can start immediately)".
|
|
76
|
+
|
|
77
|
+
**Status:** ready-for-agent
|
|
78
|
+
|
|
79
|
+
- [ ] Acceptance criterion 1
|
|
80
|
+
- [ ] Acceptance criterion 2
|
|
81
|
+
|
|
82
|
+
</local-ticket-template>
|
|
83
|
+
|
|
84
|
+
<issue-template>
|
|
85
|
+
|
|
86
|
+
## Parent
|
|
87
|
+
|
|
88
|
+
A reference to the parent issue on the tracker (if the source was an existing issue, otherwise omit this section).
|
|
89
|
+
|
|
90
|
+
## What to build
|
|
91
|
+
|
|
92
|
+
The end-to-end behaviour this ticket makes work, from the user's perspective, not layer-by-layer implementation.
|
|
93
|
+
|
|
94
|
+
## Acceptance criteria
|
|
95
|
+
|
|
96
|
+
- [ ] Criterion 1
|
|
97
|
+
- [ ] Criterion 2
|
|
98
|
+
|
|
99
|
+
## Blocked by
|
|
100
|
+
|
|
101
|
+
- A reference to each blocking ticket, or "None (can start immediately)".
|
|
102
|
+
|
|
103
|
+
</issue-template>
|
|
104
|
+
|
|
105
|
+
In either form, avoid specific file paths or code snippets: they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts, not a working demo, just the important bits.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unslop
|
|
3
|
+
description: Remove AI writing patterns from prose using either audit-only detection or a two-pass rewrite flow (diagnosis then reconstruction). Use this skill when editing, reviewing, or rewriting AI-generated content to make it sound human. Triggers on requests to "humanize", "de-slop", "fix AI text", "make it sound human", "remove AI patterns", or when reviewing text that contains obvious AI tells like "Here's the thing:", "Let that sink in", or "In today's fast-paced landscape". Also use when the user pastes text and says it "sounds like ChatGPT", "sounds robotic", "needs to sound more natural", or asks you to "clean up" drafted content before publishing.
|
|
4
|
+
license: MIT
|
|
5
|
+
user-invocable: true
|
|
6
|
+
argument-hint: "[teach · cleanup · rewrite · mimic] [input]"
|
|
7
|
+
metadata:
|
|
8
|
+
author: claytonkim
|
|
9
|
+
version: "2.3.0"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Unslop
|
|
13
|
+
|
|
14
|
+
Humanize AI-generated prose. Audit first. Rewrite only when the user asks for a rewrite.
|
|
15
|
+
|
|
16
|
+
For every audit or rewrite, read [references/core-contract.md](references/core-contract.md).
|
|
17
|
+
It is the single behavior contract. Command files define routing and mechanics;
|
|
18
|
+
presets supply optional voice, but neither can override the core contract.
|
|
19
|
+
|
|
20
|
+
## Routing
|
|
21
|
+
|
|
22
|
+
**When the user invokes a sub-command (`/unslop teach ...`, `/unslop cleanup
|
|
23
|
+
...`), you MUST read `references/commands/<command>.md` before acting.
|
|
24
|
+
Non-optional — the command file defines the flow, and skipping it drops steps the
|
|
25
|
+
user expects.** A bare `/unslop <text>` with no leading command word defaults to
|
|
26
|
+
`rewrite`. If the first word does not match a command but the intent clearly maps
|
|
27
|
+
to one (e.g. "flag the AI tells, don't change anything" → `cleanup` report-only),
|
|
28
|
+
load that command file and proceed as if invoked.
|
|
29
|
+
|
|
30
|
+
| Command | Purpose | File |
|
|
31
|
+
|---------|---------|------|
|
|
32
|
+
| `rewrite` | Default two-pass de-slop: diagnose, reconstruct under the guards, validate. | [references/commands/rewrite.md](references/commands/rewrite.md) |
|
|
33
|
+
| `cleanup` | Co-writer: cheap detection, reviewable suggestions with contract gates; includes report-only "flag, change nothing". | [references/commands/cleanup.md](references/commands/cleanup.md) |
|
|
34
|
+
| `teach` | Agent-driven voice building: harvest, approve, profile, layered card, scored demo. | [references/commands/teach.md](references/commands/teach.md) |
|
|
35
|
+
| `mimic` | Voiced drafting or rewriting under the full gates; refine loop when one pass falls short. | [references/commands/mimic.md](references/commands/mimic.md) |
|
|
36
|
+
| _maintenance_ | Turn a wild AI-ism into an eval row and a PR (not a top-level verb). | [references/commands/contribute.md](references/commands/contribute.md) |
|
|
37
|
+
|
|
38
|
+
### Routing by phrase
|
|
39
|
+
|
|
40
|
+
Sub-flows are reachable by their natural names without being top-level verbs.
|
|
41
|
+
When the user says any of these, load the named file and jump to the flow:
|
|
42
|
+
|
|
43
|
+
| The user says | Go to |
|
|
44
|
+
|---------------|-------|
|
|
45
|
+
| `audit` / "just flag it" / "don't change anything" | [references/commands/cleanup.md](references/commands/cleanup.md#report-only) |
|
|
46
|
+
| `review` / "review this before I publish" | [references/commands/cleanup.md](references/commands/cleanup.md#report-only) |
|
|
47
|
+
| `harvest` / "what writing of mine do you have?" | [references/commands/teach.md](references/commands/teach.md#1-gather-samples-harvest) |
|
|
48
|
+
| `calibrate` / "the A/B game" / "quiz me on my voice" | [references/commands/teach.md](references/commands/teach.md#calibrate) |
|
|
49
|
+
| `refine` / "keep pushing until it sounds like me" | [references/commands/mimic.md](references/commands/mimic.md#refine) |
|
|
50
|
+
| voice check / "does this sound like me?" | [references/commands/mimic.md](references/commands/mimic.md#voice-check) |
|
|
51
|
+
| "found a new AI-ism" / "add this tell" | [references/commands/contribute.md](references/commands/contribute.md) |
|
|
52
|
+
|
|
53
|
+
## Interface
|
|
54
|
+
|
|
55
|
+
| Argument | Description | Default |
|
|
56
|
+
|----------|-------------|---------|
|
|
57
|
+
| `--preset` | Voice style: `crisp`, `warm`, `expert`, `story` | `crisp` |
|
|
58
|
+
| `--strict` | Fail if rubric score < 32/40 | false |
|
|
59
|
+
| `--report` | Flag AI patterns without changing the text (cleanup) | false |
|
|
60
|
+
| Input | Text to transform (argument, file path, or stdin) | required |
|
|
61
|
+
|
|
62
|
+
Read one preset from `presets/` before writing.
|
|
63
|
+
|
|
64
|
+
| Preset | Style | Best For |
|
|
65
|
+
|--------|-------|----------|
|
|
66
|
+
| `crisp` | Short, direct, no fluff | Technical writing, documentation |
|
|
67
|
+
| `warm` | Friendly, conversational | Emails, blog posts |
|
|
68
|
+
| `expert` | Authoritative, confident | Thought leadership, articles |
|
|
69
|
+
| `story` | Narrative flow, show don't tell | Case studies, personal posts |
|
|
70
|
+
|
|
71
|
+
Rewrite, preservation, register, and validation behavior lives only in
|
|
72
|
+
`references/core-contract.md`; do not recreate or override those rules here.
|
|
73
|
+
|
|
74
|
+
## Output Format
|
|
75
|
+
|
|
76
|
+
For a quick rewrite, return the cleaned text only. For audit-only (cleanup
|
|
77
|
+
`--report`):
|
|
78
|
+
|
|
79
|
+
```markdown
|
|
80
|
+
## Issues Found
|
|
81
|
+
|
|
82
|
+
- [Quoted issue, category, severity, why it reads as AI]
|
|
83
|
+
|
|
84
|
+
## Assessment
|
|
85
|
+
|
|
86
|
+
- [Which issues are clear problems]
|
|
87
|
+
- [Which issues are judgment calls or context-dependent]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For strict or requested analysis:
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
## Transformed Text
|
|
94
|
+
|
|
95
|
+
[The humanized version]
|
|
96
|
+
|
|
97
|
+
## Validation
|
|
98
|
+
|
|
99
|
+
- Constraints: [X]/[Y] preserved
|
|
100
|
+
- AI patterns: [N] remaining (was [M])
|
|
101
|
+
- Structure: [pass/fail]
|
|
102
|
+
- Readability: Grade [X], sentence variance [Y]
|
|
103
|
+
- Change: [X]% from original
|
|
104
|
+
- Score: [X]/40
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Reference Files
|
|
108
|
+
|
|
109
|
+
| File | When to Read |
|
|
110
|
+
|------|-------------|
|
|
111
|
+
| `references/commands/*.md` | The routed command flows (rewrite, cleanup, teach, mimic, contribute). |
|
|
112
|
+
| `references/pipeline.md` | Orchestrated tiered execution for multi-agent harnesses. |
|
|
113
|
+
| `references/taboo-phrases.md` | Authoritative phrase catalog and scanner categories. |
|
|
114
|
+
| `references/fact-preservation.md` | Constraint preservation rules. |
|
|
115
|
+
| `references/rewrite-examples.md` | Executable before/after examples. |
|
|
116
|
+
| `references/{mimic,harvest,calibrate}.md` | Voice-tool internals loaded by their routed command. |
|
|
117
|
+
| `references/{rubric,edit-library,maintenance}.md` | Strict scoring, examples, and contribution procedures. |
|
|
118
|
+
| `presets/*.md` | Voice-specific deltas. |
|
|
119
|
+
|
|
120
|
+
## Maintenance
|
|
121
|
+
|
|
122
|
+
The eval contracts define the product. Add scanner examples eval-first in
|
|
123
|
+
`evals/fixtures/contracts/scanner-examples.json`; use
|
|
124
|
+
`evals/adversarial-evals.json` for agent behavior and routing. Do not edit legacy
|
|
125
|
+
`evals/evals.json`. New patterns need a false-negative example and a
|
|
126
|
+
false-positive protection example. Agent behavior changes need a `skill` row
|
|
127
|
+
and a regenerated shared benchmark. For the
|
|
128
|
+
concrete procedures (add a phrase or structure, list current patterns, sync with
|
|
129
|
+
Wikipedia's signs-of-AI-writing page), read `references/maintenance.md`. Found a
|
|
130
|
+
new AI-ism in the wild? `references/commands/contribute.md` turns the exact
|
|
131
|
+
snippet into a contract example and a structured PR, keeping both user-confirmation gates.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Human reference distribution for scripts/silhouette_scan.py. Regenerate with: python3 evals/check_silhouette.py --reference --write. median/iqr/n are derived from the sources below; fence and weight are research-validated constants (scratchpad/research/silhouette.md). The scorer scales each metric by max(iqr, fence): the sample IQR is degenerate at zero for these one-sided tells, so the fence is the effective scale.",
|
|
3
|
+
"penalty_threshold": 1.0,
|
|
4
|
+
"iqr_floor": 0.05,
|
|
5
|
+
"sources": [
|
|
6
|
+
"evals/fixtures/silhouette/corpus/human/01_linkedin.txt",
|
|
7
|
+
"evals/fixtures/silhouette/corpus/human/02_blog_intro.txt",
|
|
8
|
+
"evals/fixtures/silhouette/corpus/human/03_howto.txt",
|
|
9
|
+
"evals/fixtures/silhouette/corpus/human/04_essay.txt",
|
|
10
|
+
"evals/fixtures/silhouette/corpus/human/05_readme.txt",
|
|
11
|
+
"evals/fixtures/silhouette/corpus/human/06_email.txt",
|
|
12
|
+
"evals/fixtures/silhouette/corpus/human/07_personal_story.txt",
|
|
13
|
+
"evals/fixtures/silhouette/corpus/human/08_technical_explainer.txt",
|
|
14
|
+
"evals/fixtures/structure/struct02_bursty_narrative.md",
|
|
15
|
+
"evals/fixtures/structure/struct04_concrete_end.md",
|
|
16
|
+
"evals/fixtures/structure/struct10_varied_openers.md",
|
|
17
|
+
"evals/fixtures/structure/struct12_moderate_signpost.md",
|
|
18
|
+
"evals/fixtures/structure/struct15_one_closer.md",
|
|
19
|
+
"evals/fixtures/structure/struct16_parallel_enumeration.md",
|
|
20
|
+
"evals/fixtures/structure/struct17_academic_roadmap.md"
|
|
21
|
+
],
|
|
22
|
+
"metric_order": [
|
|
23
|
+
"scaffold_opener_share",
|
|
24
|
+
"role_entropy_bits",
|
|
25
|
+
"heading_preview",
|
|
26
|
+
"preview_fulfillment",
|
|
27
|
+
"callback_content"
|
|
28
|
+
],
|
|
29
|
+
"metrics": {
|
|
30
|
+
"scaffold_opener_share": {
|
|
31
|
+
"median": 0.0,
|
|
32
|
+
"iqr": 0.05,
|
|
33
|
+
"fence": 0.2,
|
|
34
|
+
"weight": 2.0,
|
|
35
|
+
"n": 15
|
|
36
|
+
},
|
|
37
|
+
"role_entropy_bits": {
|
|
38
|
+
"median": -0.0,
|
|
39
|
+
"iqr": 0.05,
|
|
40
|
+
"fence": 0.8,
|
|
41
|
+
"weight": 1.0,
|
|
42
|
+
"n": 15
|
|
43
|
+
},
|
|
44
|
+
"heading_preview": {
|
|
45
|
+
"median": 0.0,
|
|
46
|
+
"iqr": 0.05,
|
|
47
|
+
"fence": 0.2,
|
|
48
|
+
"weight": 1.0,
|
|
49
|
+
"n": 1
|
|
50
|
+
},
|
|
51
|
+
"preview_fulfillment": {
|
|
52
|
+
"median": 0.0,
|
|
53
|
+
"iqr": 0.05,
|
|
54
|
+
"fence": 0.25,
|
|
55
|
+
"weight": 1.0,
|
|
56
|
+
"n": 12
|
|
57
|
+
},
|
|
58
|
+
"callback_content": {
|
|
59
|
+
"median": 0.0,
|
|
60
|
+
"iqr": 0.05,
|
|
61
|
+
"fence": 0.3,
|
|
62
|
+
"weight": 1.5,
|
|
63
|
+
"n": 8
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared cheap English-detection helpers for banned_phrase_scan.py,
|
|
3
|
+
structure_scan.py, and silhouette_scan.py. All three scanners must decline the
|
|
4
|
+
same non-English inputs, so this table and its two functions live in exactly
|
|
5
|
+
one place.
|
|
6
|
+
|
|
7
|
+
Also home to the shared prose-view helpers (tokenizer, markdown stripper,
|
|
8
|
+
paragraph splitter) used by structure_scan.py and silhouette_scan.py. Those
|
|
9
|
+
two scanners previously carried private copies of these functions that had
|
|
10
|
+
drifted from each other (structure blanked blockquote lines, silhouette
|
|
11
|
+
didn't; silhouette stripped **bold**, structure didn't). strip_markdown_for_prose
|
|
12
|
+
below is the UNION of both original code paths, gated behind flags so each
|
|
13
|
+
caller reconciles the drift deliberately rather than silently: structure_scan
|
|
14
|
+
passes blank_blockquotes=True, silhouette_scan passes strip_bold=True."""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# A small set of high-frequency English function words used only for a cheap
|
|
22
|
+
# language check. The words are chosen to be distinctively English: Spanish,
|
|
23
|
+
# French, German, etc. rarely use them, so their share of tokens is a robust
|
|
24
|
+
# signal without a language-model dependency.
|
|
25
|
+
ENGLISH_FUNCTION_WORDS = frozenset({
|
|
26
|
+
"the", "and", "is", "are", "was", "were", "of", "to", "in", "that", "it",
|
|
27
|
+
"for", "with", "on", "this", "but", "not", "you", "have", "be", "as", "at",
|
|
28
|
+
"or", "we", "they", "will", "would", "there", "their", "what", "which",
|
|
29
|
+
"when", "from", "been", "has", "had", "its", "an", "by", "our", "your",
|
|
30
|
+
"if", "than", "then", "them", "these", "those", "about", "into", "over",
|
|
31
|
+
"after", "before", "how", "why", "where", "who", "can", "could", "should",
|
|
32
|
+
"do", "does", "did", "so", "out", "just", "more", "most", "some", "such",
|
|
33
|
+
"only", "also", "because", "while", "between", "through", "during", "being",
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def english_function_share(text: str) -> float:
|
|
38
|
+
"""Share of word tokens that are common English function words."""
|
|
39
|
+
tokens = re.findall(r"[a-z']+", text.lower())
|
|
40
|
+
if not tokens:
|
|
41
|
+
return 1.0
|
|
42
|
+
hits = sum(1 for t in tokens if t in ENGLISH_FUNCTION_WORDS)
|
|
43
|
+
return hits / len(tokens)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_probably_english(text: str, threshold: float = 0.10, min_tokens: int = 15) -> bool:
|
|
47
|
+
"""Cheap English detector backing a graceful non-English decline.
|
|
48
|
+
|
|
49
|
+
Conservative on purpose: inputs below ``min_tokens`` are always treated as
|
|
50
|
+
English (too little signal to decline), and ``threshold`` is low enough that
|
|
51
|
+
even terse or ESL-flavored English clears it. Only prose with almost no
|
|
52
|
+
English function words (i.e. another language) is declined.
|
|
53
|
+
"""
|
|
54
|
+
tokens = re.findall(r"[a-z']+", text.lower())
|
|
55
|
+
if len(tokens) < min_tokens:
|
|
56
|
+
return True
|
|
57
|
+
return english_function_share(text) >= threshold
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def words(text: str) -> list[str]:
|
|
61
|
+
return re.findall(r"[A-Za-z0-9]+(?:[-'][A-Za-z0-9]+)?", text.lower())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def strip_markdown_for_prose(
|
|
65
|
+
text: str, *, blank_blockquotes: bool = False, strip_bold: bool = False
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Strip markdown structure down to a prose view for cadence/discourse
|
|
68
|
+
metrics. UNION of structure_scan's and silhouette_scan's original
|
|
69
|
+
strippers -- each original branch is preserved verbatim, gated behind the
|
|
70
|
+
flag that reproduces that caller's exact prior behavior.
|
|
71
|
+
|
|
72
|
+
blank_blockquotes=True reproduces structure_scan's prior strip (blanks
|
|
73
|
+
both blockquote and heading lines in one combined check). strip_bold=True
|
|
74
|
+
reproduces silhouette_scan's prior strip (also collapses **bold** markers
|
|
75
|
+
after list/ordinal stripping).
|
|
76
|
+
"""
|
|
77
|
+
text = re.sub(r"```[\s\S]*?```", "\n\n", text)
|
|
78
|
+
kept = []
|
|
79
|
+
for line in text.splitlines():
|
|
80
|
+
if blank_blockquotes:
|
|
81
|
+
if re.match(r"\s*>", line) or re.match(r"\s{0,3}#{1,6}\s+", line):
|
|
82
|
+
kept.append("")
|
|
83
|
+
continue
|
|
84
|
+
else:
|
|
85
|
+
if re.match(r"\s{0,3}#{1,6}\s+", line):
|
|
86
|
+
kept.append("") # drop heading text from the prose view
|
|
87
|
+
continue
|
|
88
|
+
line = re.sub(r"^\s*[-*+]\s+", "", line)
|
|
89
|
+
line = re.sub(r"^\s*\d+[.)]\s+", "", line)
|
|
90
|
+
if strip_bold:
|
|
91
|
+
line = re.sub(r"\*\*([^*]+)\*\*", r"\1", line)
|
|
92
|
+
kept.append(line)
|
|
93
|
+
return "\n".join(kept)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def paragraphs(
|
|
97
|
+
text: str, *, blank_blockquotes: bool = False, strip_bold: bool = False
|
|
98
|
+
) -> list[str]:
|
|
99
|
+
stripped = strip_markdown_for_prose(
|
|
100
|
+
text, blank_blockquotes=blank_blockquotes, strip_bold=strip_bold
|
|
101
|
+
)
|
|
102
|
+
return [
|
|
103
|
+
re.sub(r"\s+", " ", p).strip()
|
|
104
|
+
for p in re.split(r"\n\s*\n", stripped)
|
|
105
|
+
if p.strip()
|
|
106
|
+
]
|