feat: approved modelling and checkable quotes - #47
Draft
david-hudec-networg wants to merge 10 commits into
Draft
Conversation
Each is independent of the others and of anything else; together they are what
can be fixed without touching how templates are generated.
The hypotheses tree used an {id, text, ...} node and array children where the
schema wants HY-keyed maps. The process wrote an id property inside the item,
where the ID is the key and Process declares none. Both scaffolds were teaching
a shape the bundled validator refuses, which is worse than being merely broken:
the natural reading is that the schema is stricter than the docs, so you hand-
write around a validator that was right.
The entities schema set templateDefaults.entities.type to "business-object".
Entity has no type property and sets additionalProperties: false, so that one
default was enough on its own to keep every scaffolded entities document
invalid.
Verified: hypotheses and process now scaffold and validate clean. entities
still fails on an unrelated defect - the generated templates carry no property
information at all - which is the next change in this series.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This is the defect behind most of the invalid scaffolds. One line in
transformTemplateData:
properties: [], // every section, every type
TemplateSection never carried properties and the transform hardcoded an empty
list, so TEMPLATE_DATA held nothing for anything. The scaffolder's default path
builds an item from section.properties.filter(required), which is always empty,
and writes a bare key:
terms:
TM01000:
TM01000: with an empty body parses as null, not an object, and validate rejects
a file add has just written. The types that looked healthy were the ones with a
hand-written example in items.ts; nothing else stood between the generator and
the output.
Extraction now finds the schema for one item of a section - patternProperties
for an ID-keyed map, items for a sequence, either inline or behind a $ref into
types/ - and records name, type, description, required, enum values and default.
resolveRef returns null rather than throwing on a shape it does not recognise,
so an unfamiliar reference degrades to the previous behaviour instead of
breaking generation.
Three things fell out of reading the schema properly. Reference placeholders
were always AC#####, whatever the field accepts, so a scaffolded step link
failed its own pattern check; the prefix now comes from the referenced
definition's own pattern, StepRef declaring ^ST\d{5,}$. Required array and
object properties were filled with the string 'TODO'. And section ID prefixes
were matched with [A-Z]{2}, so roiAnalyses (ROI#####) became RO#####.
Sequence sections are still written as maps after this; that is the next commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
links and glossary categories are YAML sequences in the schema, and the scaffolder had one way of emitting a section, so it wrote them as maps keyed by an ID that does not belong there. They now scaffold as an empty list with the item shape in comments, rather than as a sample row. A links item requires from and to, and any value there points at step IDs the user has not created yet, so a populated sample fails cross-reference validation the moment it is written. An empty list validates and the comment still teaches the shape - including the correct reference prefix, which the previous commit made available. With this, every document type add can create scaffolds a document validate accepts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Asserts the property that was missing the whole time: scaffold a document of each type, then validate the workspace it landed in. It can fail. Rebuilt from pre-fix sources, all 13 cases fail; with the series applied, all 13 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three defects compounding, all of which made the answer wrong without saying so. It wrote its own answer back to the cache, so asking what the next id would be consumed it. Three calls in a row gave three answers, each higher. A query that mutates the state it reports has no honest use. writeIdStats kept only the larger of the cached and the supplied value, so a scan could never lower a stale entry - which is precisely what syncids exists to do. Reported "AC: 1020 -> 11" and wrote nothing. And minStart was applied even when ids already existed, so a workspace numbered from AC00001 was told its next actor was AC01000. That floor is documented as the starting number for add templates; applying it to a continuation query leaves a 989-wide hole and two disjoint ranges in one document. The consequence was a command that printed Highest: AC00008 and Next ID: AC01020 one line apart and meant both. Following it numbers a model from another branch's high-water mark. Tests cover all three: the query is idempotent, a scan can lower a cached value, and a continuation continues. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every other element type could cite the insights behind it. Strategy could not, so a strategy document's provenance was prose the validator never saw - the one document in a workspace where the pipeline's guarantee did not hold, and no way to tell from inside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
david-hudec-networg
added a commit
to david-hudec-networg/skills
that referenced
this pull request
Sep 8, 2026
The skill said to verify a source's file pointer by hand when a source is added. TALXIS/ubml#47 validates it, so the instruction was the workaround-in-prose this plugin has been removing all along - it just happened to be describing a gap we had since closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Which insight is next, in the order the material was produced, with the source text beside the claim and a position counter. And recording the answer once a human has given one. It stops at bookkeeping on purpose. Reading a reviewer's reply, judging whether an ambiguous answer counts, noticing that a correction changed the meaning rather than the wording - none of that is a CLI's job. The payload and the state are mechanical; the judgement is not. set rewrites one line rather than reserialising. A workspace is a document a human is reading: its comments, its quoting and its line breaks are content, and round-tripping them through the serializer would rewrite a file underneath the reviewer. Tests cover it - one line changes, a comment survives, and setting a status and setting it back returns the original bytes. A restatement means a claim repeated from a source already walked, so it points at an insight belonging to an earlier source. Counting every related link instead would tell a reviewer "15 insights, 13 restating" on a workspace where almost nothing restated - worse than silence, because the map exists so they can pace themselves. The ordering, the labelled blocks, evidence before claim, position ahead of identity and the ID last were all prose in the consulting plugin's validate-insights skill. They are assertions here now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
david-hudec-networg
force-pushed
the
feat/proposed-model-elements
branch
2 times, most recently
from
September 8, 2026 07:11
447f8d1 to
e4133c9
Compare
david-hudec-networg
added a commit
to david-hudec-networg/skills
that referenced
this pull request
Sep 8, 2026
U6 is fixed in TALXIS/ubml#47: walk next shows the elements a claim proposes and walk set records either half. The skill described that format in prose because the tool could not do it. It points at the command now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
david-hudec-networg
force-pushed
the
feat/proposed-model-elements
branch
9 times, most recently
from
September 8, 2026 18:09
ec35432 to
0c00113
Compare
…oved DD-008 gave elements `derivedFrom`, which closed traceability. It did not close approval, and the two are different guarantees. Running the pipeline end to end showed the gap. A stakeholder confirmed 185 insights one at a time; the model was then derived from them in a single pass of 41 elements, and that pass decided things the evidence had not - that two roles named separately in the sources are one actor, that three ways of starting a request converge on one process, and which of three things a word meant every time the sources used it for all three. Every element carried a valid `derivedFrom`. Not one of those decisions had been reviewed, and nothing in the workspace could tell you so. `reviewStatus` is `proposed`, `accepted` or `rejected`, on every element type that can cite evidence. Every one: actors, entities, processes, steps, KPIs, value streams, capabilities, hypothesis nodes, hypothesis trees and glossary terms. A rule that skipped a type would be a rule with a hole in it, and the types most likely to be skipped are the ones where the judgement is largest - framing the problem, and deciding that three words name one thing. Value streams, capabilities, hypothesis trees and glossary terms gain `derivedFrom` too, because approval without provenance records that somebody agreed and not what they agreed to. A hypothesis node and tree already carry `status`, which reports whether the hypothesis is turning out to be true. That is a different question from whether anybody agreed to model it, and both are worth asking, so both fields are present and the schema says which is which. **Absent means not specified, and nothing more** (P4.4). Claiming that silence means accepted would manufacture exactly the false comfort this decision exists to remove, and P4.4 forbids it in its own words. Tooling that gates on approval looks for an explicit `accepted`; tooling that surfaces unreviewed work looks for an explicit `proposed`. A rejected element is kept, not deleted. The record that a decision was considered and turned down is what stops it being proposed again next quarter - the same reasoning as a `disputed` insight. DD-012 records the decision and answers the two rules it appears to cross. P1.4 assigns proposed-versus-current to folders and git, but its "current/proposed" are as-is and to-be states of a process, two models of a business rather than one model awaiting sign-off; the collision is on the word. DD-011 rejected a stored `maturity` because completeness is computable from what is present - approval is not, and no amount of reading a model reveals whether a person agreed with it.
P12.2 says the workspace catalogs where information lives, not the information, and names transcripts among what stays out. Its rationale is size, and that holds for the artifact but not for its text. A source entry exists so a claim can be checked against what was read. In one engagement that check could not be made: the workspace pointed at a document editable in place, so the evidence could drift under the model, and all five sources held a URL in `file` - a property documented as a path relative to the declaring document. Both validated cleanly. Five meeting transcripts are under 150KB. The recordings they came from are three orders of magnitude larger. The size argument was about the wrong noun. The schema already had both fields, so nothing is added: `url` names the artifact, `file` is the text stored beside the workspace, and `validate` now enforces what `file` always meant. A URL there is an error naming `url` instead; a path resolving to nothing is an error, because a dangling pointer looks like filed evidence and is not. A source with no `file` stays valid - a corridor conversation has no artifact. Conversion stays out of UBML. Which tool gets text out of a document or a recording depends on the format and is solved elsewhere; the pipeline picks, and records which in `notes`, because a quote that reads oddly may be the speaker or may be the converter and a reader cannot tell without being told. The example workspace gains the transcript it always claimed to have. It declared a `file` that was never written, which the new check caught on its own repository - a dangling pointer in the one place a reader looks to learn the convention. DD-013 amends P12.2 in place. The cost it accepts is that source text moves into git, and that is not a boundary the repo had already crossed: in that engagement 185 insights quote about 40,000 selected characters, while the transcripts hold everything nobody chose to extract. Reach and erasure are both worse in git than in a document store, so storing source text is a per-workspace decision the workspace states, not a language-level default.
The tool recorded an insight's status and left the element's `reviewStatus` to prose in a skill, which is the workaround-in-markdown this work has otherwise been removing. `walk next` now shows the elements a claim proposes, and `walk set` records either half: the id says which decision is being made, so an insight takes `validated`, `disputed` or `deferred` and an element takes `accepted` or `rejected`. Reviewing a claim without the element it would create approves the extraction and leaves the interpretation unasked, which is the failure the change exists to stop. It shows the element in full. A reviewer asked to approve "a term, called X" can only approve a name; the definition, the attributes, what it relates to and which claims it rests on were all still in the file. Every property is listed, nested ones included, prose set as prose and ids picked out, so the answer is about the content. An insight can be reviewed and still not resolved, so `status` gains `deferred`. `proposed` was carrying two meanings - nobody has looked at this yet, and somebody looked and could not settle it. The walk reads the first, so a claim held open on purpose is offered again every time and the review cannot get past it. Deferring is an answer: the reviewer reached the claim and decided not to decide, usually because confirming it needs somebody who was not in the room. It is not `disputed`, which says the claim is contested. A hypothesis tree gains an id type. Every prefix comes from a ref definition and there was none for HT, so a tree could be written and validated but never referenced, and anything resolving an id fell through to "element". Elements carrying any `reviewStatus` are addressable, not only proposed ones: a reviewer who accepts by mistake has to be able to say so. `next` still offers only the unanswered. Five defects came out of running it against a real workspace rather than a fixture. A step lives inside a process, so the block finder cannot assume a fixed indent, and a block must end at the next element id at any depth or a parent inherits its children's state. An escape inside a template literal is resolved before RegExp sees it. Not everything states itself in `name` - a glossary entry uses `term`, a hypothesis node uses `text`. And the confirmation line counted insights after an element was set, so the number never moved and the command read as a no-op.
david-hudec-networg
force-pushed
the
feat/proposed-model-elements
branch
from
September 8, 2026 19:13
0c00113 to
823165e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the improvements PR. It proposes a language change and amends a
recorded principle, so it is kept apart from
#46, which is bug fixes and should not
wait on a design debate.
Stacked on #46, so it shows that PR's commits too until it merges. The three
commits here are the last three.
One thesis: a workspace should be able to tell you what in it has been
approved, and let you check a quote against what was actually read. Today it
can do neither.
Where this came from
[@TomasProkop], reading the consulting plugin:
Both land, and the gap is measurable. On the engagement that prompted this, a
stakeholder confirmed 185 insights one at a time. The model was then derived
from them in a single pass of 41 elements — and that pass decided things the
evidence had not: that two roles named separately in the sources are one actor,
that three ways of starting a request converge on one process, and which of
three things a word meant, every time the sources used it for all three.
Every element carried a valid
derivedFrom. Not one of those decisions hadbeen reviewed, and nothing in the workspace could tell you so. Traceability
and approval are different guarantees and the language had a field for one.
1.
reviewStatus— approval of the modelling decisionproposed,acceptedorrejected, on every element type that can citeevidence: actors, entities, processes, steps, KPIs, value streams,
capabilities, hypothesis nodes, hypothesis trees and glossary terms.
Every one, because a rule that skips a type is a rule with a hole in it — and
the types most likely to be skipped are where the judgement is largest. Framing
the whole problem is a hypothesis tree. Deciding that three words the sources
use interchangeably name one thing is a glossary term, and getting it wrong
renames things across every other document. Those four types gain
derivedFromtoo: approval without provenance records that somebody agreed and not what to.
Absent means not specified, and nothing more (P4.4). Reading silence as
accepted would manufacture the false comfort this exists to remove.
A rejected element is kept, not deleted. The record that a decision was
considered and turned down is what stops it being proposed again next quarter.
DD-012 answers the two rules this appears to cross. P1.4 assigns
proposed-versus-current to folders and git — but its "current/proposed" are
as-is and to-be states of a process, two models of a business rather than one
model awaiting sign-off; the collision is on the word. DD-011 rejected a
stored
maturitybecause completeness is computable from what is present.Approval is not: no amount of reading a model reveals whether a person agreed.
2.
file— the text a quote can be checked againstP12.2 keeps information out of the workspace, on the grounds of size. That holds
for the artifact and not for its text. Five meeting transcripts are under 150KB;
the recordings are three orders of magnitude larger.
The schema already had both fields and they were not being used as intended.
urlnames the artifact,fileis the text stored beside the workspace, andvalidatenow enforces whatfilealways meant: a URL there is an error namingurlinstead, and a path resolving to nothing is an error, because a danglingpointer looks like filed evidence and is not.
The check found one on this repository. The example workspace declared a
transcript that had never been written — in the one place a reader looks to
learn the convention. It is written now.
Conversion stays out of UBML, and the pipeline records which converter it used,
because a quote that reads oddly may be the speaker or may be the converter.
3.
ubml walk— the two halves reviewed togetherwalk nextshows a claim beside the elements it proposes;walk setrecordseither half, the id saying which decision is being made. Reviewing a claim
without the element it would create approves the extraction and leaves the
interpretation unasked.
It shows the element in full. A reviewer asked to approve "a term, called X"
can only approve a name.
statusgainsdeferred.proposedwas carrying two meanings — nobody haslooked at this yet, and somebody looked and could not settle it. Deferring is an
answer: the reviewer reached the claim and decided not to decide, usually
because confirming it needs somebody who was not in the room.
Does it work
It was run to completion on the engagement above: 70 of 70 elements reviewed,
one at a time, 69 accepted and one rejected.
The review changed the model rather than ratifying it. Two entities turned out
to be one thing in two roles. A process was split, because a monthly invoice is
not a late step in one customer's request. A step left the model entirely
because it happens outside the application. One glossary entry claimed the
sources named a thing they never mention. One was rejected outright as a
platform concern a business twin has no reason to carry — and kept, with the
reason, so it is not proposed again.
None of that was visible before the elements were walked one at a time. That is
the argument.
Every defect the run exposed is fixed in these commits rather than described in
a skill: block finding that assumed a fixed indent, a parent inheriting its
children's state, a template-literal escape resolved before RegExp saw it,
elements that state themselves in
termortextrather thanname, aconfirmation line that counted the wrong thing, and a hypothesis tree with no id
type — writable and validatable, but impossible to reference.
npm test: 293 passed, 3 skipped.