Skip to content

Commit 2515bd8

Browse files
haabeclaude
andcommitted
0.190.0: reviewed markers — a judgement the check can read
Five keys under one convention (engine/canvas-guidance.yml#reviewed_markers): stale_prose_reviewed, handles_checked, alignment_reviewed, label_reviewed, reply_not_owed / owed: false. Each dated with a reason; content in the value. check_source_class_fidelity reports reviewed blocks as coverage and names a repeating reason as a missing rule. Unreadable dates in check_reply_owed are now spoken. Schema: provenance accepts the two markers. One test per marker. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyhzM8YE8W4QkdT4WoDuGC
1 parent 72e4b7b commit 2515bd8

15 files changed

Lines changed: 515 additions & 78 deletions

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/ai-system-card.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This is not a marketing document, a technical whitepaper, or a compliance certif
1111
## 1. Identity
1212

1313
- **System name:** Mycelium — Theory-Guided Agentic Product Development Framework
14-
- **Version:** 0.189.0 (canonical source: the `*Version X.Y.Z` line in `CLAUDE.md`; mechanical tokens here — version, skill count — are kept in sync by `scripts/sync_derived.py`, not hand-edited)
14+
- **Version:** 0.190.0 (canonical source: the `*Version X.Y.Z` line in `CLAUDE.md`; mechanical tokens here — version, skill count — are kept in sync by `scripts/sync_derived.py`, not hand-edited)
1515
- **Last updated:** 2026-06-11 (fifth audit — `/xai-check` refresh disclosing the autonomous operational mode + the `autonomous-evidence-guard`; see §9 / `services.yml :: svc-mycelium.xai.remediation_history`)
1616
- **Maintained by:** Håvard Bartnes (haabe). Issues + correspondence: [github.com/haabe/mycelium/issues](https://github.com/haabe/mycelium/issues)
1717
- **AI Act risk tier:** **Limited** (canonical, assessed 2026-05-04 by `/regulatory-review` — see `canvas/threat-model.yml :: regulatory_classification` for the full assessment). Mycelium is not in any EU AI Act Annex III high-risk category. AI outputs reach end users (developers) in user-affecting ways via the runtime, so Article 50 transparency obligations apply and are satisfied by this card + README + CLAUDE.md framing + runtime-level disclosure.

docs/changelog.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,32 @@
44
**Time to read**: 10 min.
55
**Last updated**: 2026-09-09.
66

7+
## v0.190.0 - a judgement the check can read
8+
9+
The first advisory block the dogfood agent ever received through the harness (DL-1253, after
10+
0.185.0 repaired delivery) carried four lines it ruled false positives on a read: a bug
11+
description flagged as a bare "today"; a person flagged as not a person because his quote
12+
contained "subagent"; eleven provenance blocks whose classes are listed as a set rather than
13+
index-parallel; an inbound that asked nothing, logged three times as owing no reply. None of the
14+
four checks had a way to record the ruling, so each would fire at every start until it did, and
15+
the advisory ledger from 0.184.0 cannot see a judged-and-left case, because a judged case never
16+
clears. That is the ledger's stated blind spot, and this release is the marker it was waiting for.
17+
18+
**One convention, five keys.** `engine/canvas-guidance.yml#reviewed_markers`: content in the
19+
value and never in the key name, so a marker can be dated and compared; every marker carries a
20+
date and a reason, because a marker with neither is a mute and a mute looks like coverage.
21+
`stale_prose_reviewed: 'YYYY-MM-DD <reason>'` silences both stale-prose rules on that record.
22+
`handles_checked: 'YYYY-MM-DD <what was checked>'` silences both authenticity rules.
23+
`alignment_reviewed: {date, reason}` on a provenance block reports an unequal-length block as
24+
reviewed coverage rather than FAIL, and the report names it when several blocks share one reason,
25+
because a reason that repeats is a rule the check is missing, not that many judgements.
26+
`label_reviewed: [{index, date, reason}]` skips one source's label. `reply_not_owed: {date,
27+
reason}` on a task, or `owed: false` on one inbound touch, counts inbounds dated on or before that
28+
date as answered, so a later inbound on the same task still fires.
29+
30+
**Tests.** One case per marker in the existing test files for the four checks, plus the
31+
repeating-reason line. No behaviour changes for records without a marker.
32+
733
## v0.189.0 - "did it run" is the first SLI
834

935
The dogfood `/dora-check` on 2026-09-10 rated reliability as "2 of 3 SLOs at 100%" for the month in

plugins/mycelium/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
33
"name": "mycelium",
4-
"version": "0.189.0",
4+
"version": "0.190.0",
55
"description": "I realized I let the agent start coding before I was sure it was working on the right thing, so I built this. Now it doesn't get to write code before there's a reason. The agent asks who this is for. It asks what you're assuming, and what would show you wrong. The rules run as hooks, so the build stays shut until the answers are there. Purpose, strategy, opportunities, then delivery and market feedback. The framework contains 61 skills and 13 theory gates. It adds the skills, agents and hooks, but won't touch files in a repo that already exists unless instructed to.",
66
"author": {
77
"name": "H\u00e5vard Bartnes",

plugins/mycelium/engine/canvas-guidance.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,46 @@ prior_findings_first:
895895
register would need its own reader — the same defect one layer up. Findings stay where
896896
the instrument already writes them; what changes is that the next run must read them.
897897
898+
reviewed_markers:
899+
# ADDED 2026-09-10 (v0.190.0), from the first advisory block the dogfood agent ever received
900+
# through the harness (DL-1253). Four of its lines were judged false positives on a read and
901+
# had no way to say so: a bug description flagged as a bare "today", a person flagged as not a
902+
# person because his quote contained "subagent", eleven provenance blocks whose classes are
903+
# listed as a set, and an inbound that asked nothing. Each would fire at every start until its
904+
# check learned a marker. The advisory ledger (sol-006d) measures whether a condition CLEARED;
905+
# a judged-and-left case never clears, which is the ledger's stated blind spot. These markers
906+
# are how a human judgement becomes a state the check can read.
907+
#
908+
# THE RULE: content in the VALUE, never in the key name. `stale_prose_reviewed: '2026-09-10
909+
# bug description'` can be dated and compared; `stale_prose_reviewed_2026_09_10:` can only be
910+
# grepped and never expires. Every marker carries a date and a reason; a marker with neither
911+
# is a mute, and a mute looks like coverage.
912+
markers:
913+
- check: check_stale_prose.py
914+
key: stale_prose_reviewed
915+
shape: "'YYYY-MM-DD <reason>' (record-level)"
916+
effect: both rules silent on that record
917+
- check: check_source_authenticity.py
918+
key: handles_checked
919+
shape: "'YYYY-MM-DD <what was checked and how>' (record-level)"
920+
effect: rules A and B silent on that record
921+
- check: check_source_class_fidelity.py
922+
key: alignment_reviewed
923+
shape: "{date: YYYY-MM-DD, reason: <why the arrays differ>} (on the provenance block)"
924+
effect: unequal-length block reported as reviewed coverage, never FAIL
925+
- check: check_source_class_fidelity.py
926+
key: label_reviewed
927+
shape: "[{index: N, date: YYYY-MM-DD, reason: <why the label stands>}] (on the provenance block)"
928+
effect: that index skipped by the contradiction detector
929+
- check: check_reply_owed.py
930+
key: reply_not_owed
931+
shape: "{date: YYYY-MM-DD, reason: <what the inbound was>} (task-level); or `owed: false` on one inbound touch"
932+
effect: inbound touches dated on or before the date count as answered; a later inbound still fires
933+
what_a_marker_is_not: >-
934+
A marker is a ruling on ONE record on ONE date. It is not a way to turn a check off, and a
935+
check that finds many markers with the same reason should say so in its report, because a
936+
reason that repeats is a rule the check is missing, not eleven judgements.
937+
898938
learning_target_coupling:
899939
principle: |
900940
When a feedback-gathering activity is being designed (an interview, an

plugins/mycelium/harness/fail-open-reviewed.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,19 @@ reviewed:
501501
"advisory ledger module unavailable"; pick() joins the notes and main() prints them to STDOUT
502502
(the hook drops stderr), so the line the human sees names what could not be read rather than
503503
presenting a lesser item as the whole picture. Reviewed 2026-09-10, v0.188.0.
504+
505+
- id: check_reply_owed.py@ce4f01f7f1
506+
verdict: accepted
507+
reason: >-
508+
`_entry_date`: a touch_log date that is not ISO is skipped when ordering contacts, and
509+
the skip is SPOKEN: the handler appends "ht-id touch_log[i]: <raw>" to UNREADABLE_DATES,
510+
which main() prints as a NOTE line (and as `unreadable_dates` under --json), so an
511+
inbound hidden by a typo'd date is named rather than passed over. Reviewed 2026-09-10,
512+
v0.190.0.
513+
514+
- id: check_reply_owed.py@1db3251134
515+
verdict: accepted
516+
reason: >-
517+
`_reply_not_owed_date`: a `reply_not_owed.date` that is not ISO means the marker does
518+
not apply (the safe direction: the inbound still fires), and the handler appends the
519+
raw value to UNREADABLE_DATES, which main() prints. Reviewed 2026-09-10, v0.190.0.

plugins/mycelium/schemas/canvas/_common.schema.json

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"test-validated",
1414
"launch-validated"
1515
],
16-
"description": "Gilad's evidence ladder. Match the level of evidence to the claim \u2014 never overclaim."
16+
"description": "Gilad's evidence ladder. Match the level of evidence to the claim never overclaim."
1717
},
1818
"confidence": {
1919
"oneOf": [
@@ -43,7 +43,7 @@
4343
"internal_simulated",
4444
"pointer"
4545
],
46-
"description": "Classification of an evidence source's origin. external_human = real conversation with a real person (user interview, usability test, survey response). external_data = third-party analytics, behavioral data, market data. internal_stakeholder = founder/PM/team interview \u2014 beliefs about users, constraints, strategy; organizational mythology (Brown). Counts toward L0 confidence but does NOT satisfy L2 external_human requirements. internal_desk = desk research, competitive analysis, brainstorming. internal_simulated = mocked persona interview, thought experiment. pointer = the source is a REFERENCE to another canvas entry, not evidence in itself (e.g. `purpose.yml#evidence (2026-05-03 \u2026)`); its class is whatever the target carries. Added v0.82.0 after a dogfood classification pass: copying the target's class into the pointer looks tidier and goes stale silently the moment the target is reclassified \u2014 the same duplicate-a-derived-value defect that produced a stale correction count and a stale gate-heading the same week. A pointer is RESOLVED AT READ TIME. An unresolved pointer (target unclassified or missing) is reported as unresolved and is NOT defaulted to internal_desk, because a conservative default here would silently understate external coverage and read as a measurement."
46+
"description": "Classification of an evidence source's origin. external_human = real conversation with a real person (user interview, usability test, survey response). external_data = third-party analytics, behavioral data, market data. internal_stakeholder = founder/PM/team interview — beliefs about users, constraints, strategy; organizational mythology (Brown). Counts toward L0 confidence but does NOT satisfy L2 external_human requirements. internal_desk = desk research, competitive analysis, brainstorming. internal_simulated = mocked persona interview, thought experiment. pointer = the source is a REFERENCE to another canvas entry, not evidence in itself (e.g. `purpose.yml#evidence (2026-05-03 …)`); its class is whatever the target carries. Added v0.82.0 after a dogfood classification pass: copying the target's class into the pointer looks tidier and goes stale silently the moment the target is reclassified — the same duplicate-a-derived-value defect that produced a stale correction count and a stale gate-heading the same week. A pointer is RESOLVED AT READ TIME. An unresolved pointer (target unclassified or missing) is reported as unresolved and is NOT defaulted to internal_desk, because a conservative default here would silently understate external coverage and read as a measurement."
4747
},
4848
"provenance": {
4949
"type": "object",
@@ -65,7 +65,7 @@
6565
"items": {
6666
"$ref": "#/$defs/source_class"
6767
},
68-
"description": "Parallel array to evidence_sources classifying each source's origin. Optional \u2014 unclassified sources default to internal_desk for ratio calculations. Added in v0.11.0 to address the internal/external evidence ratio Goodhart trap."
68+
"description": "Parallel array to evidence_sources classifying each source's origin. Optional unclassified sources default to internal_desk for ratio calculations. Added in v0.11.0 to address the internal/external evidence ratio Goodhart trap."
6969
},
7070
"source_class": {
7171
"$ref": "#/$defs/source_class",
@@ -84,11 +84,52 @@
8484
},
8585
"notes": {
8686
"type": "string",
87-
"description": "Free-text annotation about how the evidence was gathered, caveats, or context that doesn't fit other fields. Optional. Kept short \u2014 provenance is reference metadata, not narrative."
87+
"description": "Free-text annotation about how the evidence was gathered, caveats, or context that doesn't fit other fields. Optional. Kept short provenance is reference metadata, not narrative."
8888
},
8989
"validated_at": {
9090
"$ref": "#/$defs/iso_timestamp",
91-
"description": "When this evidence was last RE-CHECKED and found to still hold \u2014 distinct from `captured_at`, which is when it was first gathered and must never be overwritten. engine/evidence-decay.md makes this the field a refresh writes ('update `validated_at` to the current date'), canvas-health step 7 scans for it, and the decay comparison reads the newer of the two. DECLARED 2026-09-01 BECAUSE IT WAS NOT: this def sets additionalProperties:false, so for the life of the schema the framework instructed a refresh action that its own validator rejected. A consumer following the documented protocol produced an invalid canvas, which is a plausible reason no entry in the dogfood project was ever refreshed \u2014 the decay loop could not close."
91+
"description": "When this evidence was last RE-CHECKED and found to still hold — distinct from `captured_at`, which is when it was first gathered and must never be overwritten. engine/evidence-decay.md makes this the field a refresh writes ('update `validated_at` to the current date'), canvas-health step 7 scans for it, and the decay comparison reads the newer of the two. DECLARED 2026-09-01 BECAUSE IT WAS NOT: this def sets additionalProperties:false, so for the life of the schema the framework instructed a refresh action that its own validator rejected. A consumer following the documented protocol produced an invalid canvas, which is a plausible reason no entry in the dogfood project was ever refreshed — the decay loop could not close."
92+
},
93+
"alignment_reviewed": {
94+
"type": "object",
95+
"description": "Reviewed marker (canvas-guidance.yml#reviewed_markers): a human read this unequal-length block and judged the labels sound. Dated with a reason; check_source_class_fidelity reports it as coverage, not FAIL.",
96+
"required": [
97+
"date",
98+
"reason"
99+
],
100+
"properties": {
101+
"date": {
102+
"type": "string"
103+
},
104+
"reason": {
105+
"type": "string"
106+
}
107+
},
108+
"additionalProperties": false
109+
},
110+
"label_reviewed": {
111+
"type": "array",
112+
"description": "Reviewed marker: one source's label was read and kept. Index into evidence_sources, dated, with a reason.",
113+
"items": {
114+
"type": "object",
115+
"required": [
116+
"index",
117+
"date",
118+
"reason"
119+
],
120+
"properties": {
121+
"index": {
122+
"type": "integer"
123+
},
124+
"date": {
125+
"type": "string"
126+
},
127+
"reason": {
128+
"type": "string"
129+
}
130+
},
131+
"additionalProperties": false
132+
}
92133
}
93134
},
94135
"required": [
@@ -158,11 +199,11 @@
158199
"observed",
159200
"inferred"
160201
],
161-
"description": "How the closure reason was established. observed = something happened and was witnessed or recorded (a reply arrived, a message went unanswered, a criterion was measured and missed). inferred = the reason rests on reasoning about what probably happened, most commonly elapsed time ('the window has passed', 'no word since'). An elapsed interval is NOT an observation: nothing new is learned by a calendar advancing. Writing `inferred` is not a failure \u2014 it is the honest label for a defensible guess, and it marks the claim so a later reader does not cite it as fact. Origin: a dogfood closure recorded as fact from a month-old expectation, then cited across 12 sites before anyone asked the person who knew (2026-08-04)."
202+
"description": "How the closure reason was established. observed = something happened and was witnessed or recorded (a reply arrived, a message went unanswered, a criterion was measured and missed). inferred = the reason rests on reasoning about what probably happened, most commonly elapsed time ('the window has passed', 'no word since'). An elapsed interval is NOT an observation: nothing new is learned by a calendar advancing. Writing `inferred` is not a failure it is the honest label for a defensible guess, and it marks the claim so a later reader does not cite it as fact. Origin: a dogfood closure recorded as fact from a month-old expectation, then cited across 12 sites before anyone asked the person who knew (2026-08-04)."
162203
},
163204
"closure_discipline": {
164205
"title": "Closure discipline",
165-
"description": "Applies to any canvas item that can be closed. If a `closure_reason` is asserted, two companions become mandatory: `closure_basis` (was this observed or inferred) and `reopen_trigger` (what would make this wrong). Deliberately conditional rather than always-required \u2014 items that are simply finished need no ceremony; the obligation attaches only when a REASON is claimed. Solution leaves have had an archive protocol since v0.x (archived-solutions.yml, 'Never deleted \u2014 they are learning'); opportunities and human-tasks had none, and hand-rolled reopen semantics appeared 17 times in one dogfood canvas against 0 mentions in its schema.",
206+
"description": "Applies to any canvas item that can be closed. If a `closure_reason` is asserted, two companions become mandatory: `closure_basis` (was this observed or inferred) and `reopen_trigger` (what would make this wrong). Deliberately conditional rather than always-required items that are simply finished need no ceremony; the obligation attaches only when a REASON is claimed. Solution leaves have had an archive protocol since v0.x (archived-solutions.yml, 'Never deleted they are learning'); opportunities and human-tasks had none, and hand-rolled reopen semantics appeared 17 times in one dogfood canvas against 0 mentions in its schema.",
166207
"if": {
167208
"required": [
168209
"closure_reason"

0 commit comments

Comments
 (0)