On-Chain Tally: DAO v1.1 Limits and a Deposit-Paired Voting Proposal

The quote describes DAO v1.1, not the full CKB story.

In DAO v1.1, the latest vote, the latest binding, and the DAO weight at the snapshot must be reconstructed from history rather than read from live cells, so the tally remains off-chain.

The proposed Deposit-Paired Voting design keeps decisive state in live cells and validates it during state transitions.

Summary

  • Off-chain tally: works when the operator role and recomputation path are explicit.
  • Recomputation: independent off-chain recomputation is not the same property as self-contained on-chain tally.
  • DAO v1.1: improves auditability over DAO v1.0, but does not support self-contained on-chain tally because decisive state is still reconstructed off-chain.
  • Missing proofs: latest-state completeness (proving no later vote or binding superseded the counted one) and snapshot-time DAO liveness, not arithmetic.
  • Requirement: self-contained on-chain tally needs proposal state, voter control state, and deposit binding to live in cells that protocol rules validate.
  • Proposal: Deposit-Paired Voting keeps proposal state, controller state, and deposit binding in VoteMeta, VotingRightOwner, and VotingRightOwned. It derives a time-independent, proposal-independent protocol weight from the backing deposit in cell_deps, the deposit’s creation header, and fixed genesis accumulated rate AR_0.
  • Costs: one proposal-wide serialized update lane, one per-voting-right serialized update lane, parked-deposit custody, and a one-pending-vote confirmation model.

What Self-Contained On-Chain Tally Requires

Self-contained on-chain tally on CKB requires a small set of properties:

  • authoritative proposal state in live cells validated by protocol rules
  • authoritative voter control state in live cells validated by protocol rules
  • an explicit, locally verifiable binding between voting power and the backing asset in the current transaction
  • voting weight derived from a script-verifiable deposit, its creation header, and fixed genesis AR_0, rather than reconstructed from raw DAO liveness at vote end
  • delegation, if retained, validated by current transitions rather than reconstructed later
  • finalization rules that use only inputs, deps, headers, and since relations that CKB can actually verify through header_deps, ckb_load_header, and the since verification rule

In every case, authoritative state must live where CKB validation can read it. CKB can read specific inputs, cell_deps, headers named in header_deps, and since locks. It cannot read the full live cell set from a block header alone.

How DAO v1.1 Produces a Result

DAO v1.1 reaches a result by rebuilding state off-chain and then summing it.

The result path in build_vote_results does five things:

  1. Loads latest vote rows from all_votes. Before that step, the vote indexer has already reduced history to one latest row per address through query_all_votes and query_vote_records_by_epoch_opt.
  2. Applies the duplicate-row guard, which invalidates any ckbAddress that appears more than once in the all_votes response.
  3. Expands bindings and stake through get_weight.
  4. Applies the bound-address override rule through the self_weight_addr_set filter.
  5. Sums the resulting weights into per-candidate totals.

That tally depends on five separate claims:

  1. The vote indexer supplied the correct latest vote rows.
  2. The binding indexer supplied the correct latest binding state before vote end.
  3. The DAO indexer supplied the correct live deposit weights at the snapshot height.
  4. The bound-address override rule was applied correctly.
  5. The arithmetic is correct.

Only claim 5 is a direct arithmetic check once the counted state is fixed. The other four are reconstructed-state claims about which state may be counted.

Why DAO v1.1 Still Depends on Off-Chain Reconstruction

DAO v1.1 cannot move tally on-chain because it cannot prove final vote state, current binding state, and snapshot-time DAO liveness.

1. Vote changing turns tally into a latest-state claim

Once votes and weights can change, tally requires proof that no later change superseded the counted state before vote end.

The DAO v1.1 vote-changing requirements explicitly allow vote changing, vote cancellation, and dynamic weights. The verified tally path uses query_vote_records_by_epoch_opt, get_weight, and query_dao_stake_until_height to reduce votes to latest indexed rows and apply weight snapshots at vote end.

The vote indexer makes that latest-state choice: it scans vote outputs in vote mode, vote_output_handle inserts them into vote_record, and query_vote_records_by_epoch_opt reduces them to one latest row per address. That remains an off-chain completeness decision.

2. Existing vote cells do not provide stable tally inputs

Current vote cells are not stable tally inputs. They are built for vote submission, not later self-contained aggregation.

Current vote-building clients create vote cells with lock: voteAddr.script in both the standalone user-vote transaction builder and the frontend voting transaction builder. The contract separately checks that VoteProof.lock_script_hash matches an input lock hash.

After the fact, an on-chain tally has only two options:

  • consume vote cells as inputs: every voter must sign the tally transaction
  • reference vote cells through cell_deps: the tally transaction needs no voter signatures, but every referenced dep must still be live at validation

The second option races with ordinary spending. If the tally builder selects vote cell V as a cell_dep and the voter spends V first, the tally transaction fails because resolve_transaction rejects dead outpoints while resolving deps.

Historical proof can show the vote transaction and its witnesses against a block header, but not the previous output lock scripts. Raw transaction inputs contain only previous_output and since, not those lock scripts. A TX-in-witness approach that keeps the current rule also needs proofs for the referenced input cells and their lock scripts.

3. Binding state is reconstructed off-chain

Binding state is also reconstructed from indexed history, not read from live protocol state. The address-bind indexer reads witness-carried bind data in verify_tx, stores it in bind_info while blocks are scanned, and answers queries through query_by_to_at_height.

An on-chain tally can derive “latest binding before vote end” only if binding becomes live state validated by protocol rules, or if the tally accepts an external completeness assumption.

4. DAO weight at vote end depends on snapshot-time liveness

Snapshot-time DAO liveness also remains off-chain, because CKB headers do not commit to the live cell set.

The CKB RawHeader schema includes transactions_root, proposals_hash, extra_hash, and dao, but no Ethereum-style state root. The transactions_root calculation commits to merkle_root([raw_transactions_root, witnesses_root]), not the live cell set.

A header alone cannot prove that a deposit cell was still live at snapshot block H. That is enough for audit-oriented recomputation, but not for a self-contained on-chain tally of raw DAO-backed weight.

What DAO v1.1 Publicly Claims

DAO v1.1 publicly claims independent off-chain recomputation, not self-contained on-chain tally.

Docs: DAO v1.1’s public docs frame the target as independently verifiable results. The same docs define address weight in terms of Nervos DAO stake, take the voter list source from DAO stakers, and later filter that list to DID users. That framing matches independently recomputable off-chain tally, not self-contained on-chain tally.

Forum claim: The public claim that anyone can recompute the tally without a centralized API still describes off-chain recomputation, not self-contained on-chain tally. Recomputing the result still requires rebuilding the same off-chain latest-state pipeline: latest vote rows from query_vote_records_by_epoch_opt, latest binding state from query_by_to_at_height, and DAO stake snapshots from query_dao_stake_until_height.

Implementation boundary: The implementation also requires proposal-time latest VoterList selection, VoteMeta SMT-root construction in build_vote_meta, and backend proof issuance through get_proof and prepare before a user can submit a vote. Those steps still shape who can vote and what proof the vote carries.

Deposit-Paired Voting Proposal

Deposit-Paired Voting is a proposed design that moves decisive state into live cells without minting a separate voting token.

State Model

The proposal keeps state in three live cell types:

  1. VoteMeta: a proposal cell
  2. VotingRightOwner: a user-owned controller cell
  3. VotingRightOwned: a protocol-locked backing DAO deposit cell

A VotingRightOwner and VotingRightOwned together form one voting-right pair.

  • VoteMeta stores the confirmed tally, one pending vote slot, the deadline, the open or final state, and proposal identity after the first vote or close
  • VotingRightOwner stores owner authority, optional delegate authority, the paired backing outpoint, and per-proposal entries
  • VotingRightOwned holds the parked DAO deposit and enforces custody only

Ordinary vote updates consume and recreate VoteMeta and VotingRightOwner. They do not move the backing deposit.

Proposal id: Each proposal needs a stable id that survives VoteMeta recreation. The initial VoteMeta stores no proposal id. The first vote or close transaction reads the consumed input’s previous_output, copies that creation OutPoint into the recreated VoteMeta, and later updates preserve it.

Custody: VotingRightOwned enforces only custody invariants: the backing deposit stays paired to one controller, cannot be redirected, and can be released only through the paired release path.

Initialization and Binding

Voting-right creation must establish the pair once and keep it stable afterward.

Voting-right creation uses one transaction:

  • create VotingRightOwned as a fresh DAO deposit under Deposit and lock it with the voting-right logic
  • create VotingRightOwner in the same transaction and pair it with that deposit

Existing deposits: If the user starts from an existing user-controlled DAO deposit, it must go through the normal Withdraw Phase 1 path and then be redeposited under the voting-right lock. Deposited input handling shows that a deposited DAO input cannot be re-locked directly into another deposited cell.

Pair binding: The creation transaction can link the pair by output position. Later vote and revote updates bind it by the committed OutPoint, so the backing deposit cannot be swapped behind the controller.

Proposal creation: A proposal starts as a fresh VoteMeta with no stored proposal id, a deadline, zero confirmed tally, an empty pending vote slot, and open state.

Authority and Release

Each cell validates the invariant it owns:

  • VoteMeta owns proposal identity, tally updates, pending-vote confirmation, and the open -> final transition
  • the voting-right logic owns correct pairing of VotingRightOwner and VotingRightOwned, weight derivation from the parked deposit and its creation header against AR_0, and custody of the parked deposit
  • the owner or delegate path owns vote and revote updates to per-proposal entries and delegated voting authority

Delegation: A delegate may cast and revote on behalf of the owner, but cannot redirect the backing deposit, release the voting right, or perform unrelated state changes.

Release: VotingRightOwner and VotingRightOwned are consumed together. Release succeeds only when every proposal entry recorded in VotingRightOwner is already past its deadline under the transaction’s encoded since threshold. CKB enforces that threshold under the since verification rule and verify_absolute_lock. Release does not depend on a third party closing every touched proposal. It depends only on whether all proposals previously touched by that voting right are past their deadlines.

Derived Weight and Verifiable Timing

The design derives weight and proves timing without snapshot-time DAO liveness.

Weight rule: Weight is neither the current withdrawable CKB amount at vote end nor a value stored in VotingRightOwner. Each calculation derives it from the backing deposit in cell_deps and its creation header. The DAO accumulated-rate formula defines AR_i and genesis AR_0 : 10 ^ 16, and CKB’s maximum withdraw formula uses the same ratio. With genesis normalization, the sketch is:

weight(counted_capacity, AR_m) {
    return counted_capacity * AR_0 / AR_m
}

Consequence: The protocol weight does not vary with later time or with the proposal being voted on. That said, User Interface can still show the deposit’s current withdrawable amount.

Runtime loading: The creation transaction can pair VotingRightOwner with VotingRightOwned, but it cannot compute voting weight because it cannot load its own inclusion header. CKB scripts read only explicit header_deps. They load creation headers through ckb_load_header from referenced inputs or cell_deps. A later vote, revote, or close transition must include the backing deposit in cell_deps, load its creation header, and derive weight there against fixed genesis AR_0.

Because a vote transaction cannot prove its own deadline position at submission time, VoteMeta carries the confirmed tally plus one pending vote slot. The pending slot stores the VotingRightOwner identity and the pending choice.

Vote and revote: vote and revote consume VoteMeta and VotingRightOwner. They keep VotingRightOwned parked, verify the pair through the backing deposit in cell_deps, and derive weight from the deposit’s creation header against AR_0. If the consumed VoteMeta was created before the deadline, the previous pending vote is confirmed into the tally. If it was created after the deadline, that pending vote is discarded.

Close: close consumes VoteMeta, proves finalization timing under the since verification rule and verify_absolute_lock, reloads the pending VotingRightOwner and backing deposit from cell_deps, derives the last pending vote’s weight against AR_0, and moves the proposal from open to final.

The one-pending-vote model follows directly from what a vote transaction can and cannot prove about its own inclusion time.

Why It Fits the On-Chain Tally Requirements

Against the requirements above, the proposal stores proposal state, controller state, and deposit binding in live cells, then validates timing and weight during each transition.

Compared with DAO v1.1:

Dimension DAO v1.1 Deposit-Paired Voting
Vote state Reconstructs latest vote state off-chain Stores current vote state in live cells
Binding state Reconstructs latest binding state off-chain Stores an explicit controller-to-deposit pair in live cells
Weight derivation Needs raw DAO snapshot liveness at vote end Derives the same protocol weight from the backing deposit and its creation header against AR_0 in each transition that needs it
Result assembly Relies on an off-chain tally job to assemble final state Updates tally state inline in VoteMeta
Delegation and binding Treats delegation and binding as off-chain interpretation Validates both during current transitions

The arithmetic rule stays the same. Deposit-Paired Voting moves authoritative state into live cells.

Tradeoffs and Limits

Deposit-Paired Voting still makes some Tradeoffs:

  • one proposal-wide serialized update lane through VoteMeta
  • one per-voting-right serialized update lane through each VotingRightOwner
  • the backing DAO deposit stays parked in VotingRightOwned while the voting right exists
  • a pending vote becomes confirmed only when a later vote or close resolves it

Conclusion

The limit is not CKB: tally follows the deciding state.

In DAO v1.1, that means off-chain tally, plus all the off-chain complexity that comes with it.

Deposit-Paired Voting is a proposed design that flips that setup. Put the deciding state in live cells. Then the tally can live there too: no separate voters whitelist, no off-chain reconstruction stack to run, no snapshot-time DAO liveness requirement.

Such is the difference between a system independently verifiable and one that lives on-chain.

6 Likes

非常简单直接的full onchain 投票方案。

这个方案理论上是可行的,但是落地到真实场景会有非常多的问题。

问题太多了,这个输入框根本放不下,所以我只会提最重要的3个问题:

  1. 投票状态放在vote meta cell里。这会有cell竞争的问题,两个人同时投票一定会有一个人失败。
  2. 投票交易的构造太复杂了,gas费会非常高。
  3. 现有的NervosDao质押cell必须进行一次转换。这意味着Neuron,JoyID,CKB Explorer等等NervosDao相关的生态工具都需要进行适配,这完全不现实。

也许在ckb底层还存在更多的限制导致该方案不可行,比如header deps的数量,cell的大小等。这个需要core team进行确认。 @quake @xjd

1 Like

Hey @david-fi5box, glad it picked your interest!

Yup, thank you for picking it up!! It was briefly mentioned in Tradeoff section, but merits a deeper analisys:

  1. State contention is not a road blocker for voting at the current levels of participation, worst case scenario a user just need to sign again a tx :white_check_mark:
  2. State contention tho is an issue for DoS potential: attacker can keep busy the VoteMeta cell, effectively denying participation of others to the vote :cross_mark:

As you are well aware this is a problem felt by deeply by the entire ecosystem, so much that we had over the years attempts over attempts to fix it, namely Intent Cells, Open Transactions and (weird but intriguing) PoW gated access to cells:

  • PoW gated access to cells is still in the ideation phase, needs research
  • Open Transactions would be the cleanest solution: we have working designs, but what’s currently lacking is EcoSystem agreement.
  • Intent Cells are the best candidate: user transforms VoteRightOwner into an intent cell by using a special lock on it (very similar to Delegation in this model) and an aggregator makes sure that his vote is included.

Side Note: user can choose which Lock he prefers, we don’t need to force everyone to use Intent Cells (nor Delegation OFC). Again, if he so prefers, a user can just keep signing again until his vote is included.

Not really a thing:

  • VoteMeta just store little constant information: not who voted, but what is the aggregated result and small Vote meta information
  • VotingRightOwner stores more information: there must be an entry for each vote participated to. That said, this entry can later on be garbage collected once the vote ends, so it doesn’t grow indefinitely and it’s proportional to the number of active votes at a certain moment.

We have the following trade-offs:

  1. Want to keep current deposits? → Fully off-chain system, doable, tricky part is to make sure it’s auditable, transparent and operator resistant.
  2. Want to have an elegant simple verified on-chain system? → You need to put a special lock on those deposits, hence withdraw and re-deposit.

Anything in the middle and you get way more complexity than you bargained for.

Yesterday I was explaining @chenyukang the reasoning underpinning the presented design:

Back to us

Thank you for the feedback @david-fi5box, keep it coming :hugs:

Phroi

PS: If you prefer, I can transform this design sketch into code and we can test it out together

2 Likes

From the perspective of researching technical solutions for a full on-chain voting system alone, I am quite willing to continue exploring this approach. In fact, I have discussed potential solutions to cell conflicts with many CKB developers, including the core team, developer relations, and developers from other ecosystem partners.

There are also many details within the specific solution that can be elaborated on. For example, DAO tokens staked by users may be distributed across multiple addresses, as well as across multiple cells under a single address—how can these cells be aggregated to avoid off-chain weight indexers; if multiple proposals are being voted on simultaneously, whether this solution allows users to cast votes on multiple proposals at the same time, and so on.

However, regarding the DAO 1.1 technical solution, I believe the three issues I mentioned earlier are already serious enough.

Of course, this is related to the project positioning of DAO 1.1.

I previously mentioned in another locked post that the foundation’s original plan was to go directly from DAO 1.0 to DAO 2.0. However, DAO 2.0 was delayed due to discussions triggered by the delegation system. Therefore, the initial goal of DAO 1.1 was to use relatively conservative and mature technology to complete a deployable system as soon as possible to replace Metaforo.

A specific example is our earlier discussion about the keystore. We initially designed a very streamlined private key custody solution, but it required wallet providers to make corresponding modifications. Because this would lead to unpredictable project delays, we had to adopt the solution that everyone sees now.

I’d also like to take this opportunity to briefly explain for NightLantern from another recently locked post.

The DAO 1.1 team members are indeed feeling quite frustrated right now. However, this is not because you or others pointed out problems with the DAO 1.1 project—a truly professional programmer feels pleased, not angry, when someone identifies bugs in their code.

Rather, it’s because I am certain that some people in the community are aware of the story behind the DAO 1.1 project and the various constraints we face. Yet during community discussions, they chose not to come forward and explain the situation.

To use a perhaps imperfect analogy: your neighbor suddenly falls ill in the middle of the night, and to get them to the hospital in time, you exceed the speed limit and run red lights. But when you want your neighbor to help explain the situation to the police to avoid your ticket, their response is: every citizen should obey traffic rules.

I believe the version numbers 1.0, 1.1, and 2.0 already clearly indicate the project’s positioning. If we haven’t aligned on the project positioning, I find it meaningless to discuss the technical solution.

And discussions about project positioning have already gone beyond the scope of technology—I am currently waiting for updates. If the positioning of DAO 1.1 is not what I initially understood, I think starting a new project from scratch would be a better choice.

I suggest you open a separate thread to discuss the technical solution, without tying it to DAO 1.1.

By the way, I’ve noticed that discussion threads related to DAO 1.1 have a very high probability of being locked.

Rather than diving into technical specs, I want to talk about software engineering. I’ve spent 20+ years building software—both To B and To C. But the CKB community introduced me to something new, which I’ll call “To Community.”

In software engineering, locking down requirements is critical—it drives every downstream decision. In To B and To C, requirements are determinate. Even when end-user needs are fuzzy, someone owns the process of clarifying them, so at least for one sprint or cycle, the team knows what to build. To Community flips this. Requirements remain indeterminate. A proposal can pass, yet at delivery, voices pop up saying “this isn’t what we actually wanted.” In traditional settings, that’s a change request—you go back to the stakeholder, renegotiate, adjust. In CKB? There’s no single person accountable for requirements. So the project stalls. No one knows what “done” looks like.

What’s your take? If Rosen Bridge faced this, how would you handle it?

P.S. An AI told me: “You may need to redefine success. In To Community, ‘sustainable relationships’ might matter more than ‘shipping the right feature.’” :frowning:

1 Like

@matt_ckb Hi Matt, Phroi and I have been discussing possible alternatives in this thread. If you have any suggestions, feel free to post them here as well.

However, so far, I don’t think the solution provided by Phroi can serve as an alternative to DAO 1.1.

First, there are the unresolved technical issues mentioned earlier.

More importantly, it’s about the goals and positioning of DAO 1.1. I believe the current technical solution of DAO 1.1 is optimal and is based on the content of the DAO 1.1 proposal that has already passed community voting.

But from last month until now, much of the discussion has actually gone beyond the content of the DAO 1.1 proposal, including this alternative solution.

From its version number alone, DAO 1.1 is clearly a minor improvement to DAO 1.0 (Metaforo). Therefore, it does not involve changes to meta-rules and must inherit many legacy rules and limitations from DAO 1.0 (Metaforo).

Take this alternative solution here, for example—it requires users to convert their existing NervosDao stake cells. Isn’t this a meta-rule modification? It requires wallet and browser adaptations. Can this be delivered within just a few months?

If the premise for discussing alternative solutions now is that these constraints no longer exist, then I don’t think this is the DAO 1.1 proposal anymore. Perhaps a new proposal should be initiated and voted on again.

@matt_ckb Yes, I also think the whitelist is a huge misunderstanding. But on the contrary, I don’t think we are the ones who misunderstood.
No matter what you call it—whitelist or any other term. DAO 1.1 does not change the boundaries of voting eligibility.
1.In DAO 1.0, only those who have staked in NervosDAO have voting weight. DAO 1.1 maintains consistency with this.
2. In DAO 1.0, address binding can be used to bind the stake from Neuron wallet to a voting address. DAO 1.1 maintains consistency with this and has implemented more detailed checks for duplicate weight calculations.
3. DAO 1.0 requires logging into Metaforo to vote. DAO 1.1 requires registering a DID to vote. I believe this is equivalent and does not change the boundaries of voting eligibility. Moreover, as I mentioned in my previous reply, Metaforo is a centralized system—if the server goes down, users cannot log in. However, DID is an on-chain identity system; as long as you have a wallet, you can register, and it has no relation to the DAO 1.1 system.

@matt_ckb There is another risk of making large-scale technical changes late in the project that I must remind everyone of.
Community members may suspect that the team is using technical changes as a pretext to slip unreviewed and unvoted proposals into an already approved proposal, thereby bypassing community voting.
I recommend starting over from project positioning, goals, and requirements, and then deriving the technical solution to be used. This is also standard software engineering practice when facing requirement changes.

@matt_ckb By the way, when I said earlier that I was disappointed with certain people, I wasn’t referring to any specific individual—not Jan, not Terry, certainly not Phroi, not anyone in particular. Rather, as I mentioned in my post about software engineering, if a proposal can pass a vote, it means the majority supported it. But in the subsequent discussions, I only saw voices of opposition, with no voices of support. It’s like I took on a project, but when it was almost ready for delivery, the person who placed the order disappeared.

Of course, there may be an issue of the “silent majority” here, but I haven’t seen any measures or rules from the community in this regard.

DAO 1.1 can fail—it was originally an attempt at community governance. However, I hope the community can pay attention to the issues exposed throughout this process.

1 Like

hi @david-fi5box Let me start by saying, that the reason that 1.1-related threads are getting locked is from them straying far from the topic they started with. That is the case here, but I will manage one thing at a time.

This is a topic for discussing alternatives, I do believe that our process would be served by doing that, and improving the proposed ideas, rather than dismissing them outright and explaining generalities around 1.1.

White list comment


this refers to the conversation in January and everything that followed.

”I’m haoyang, one of the DAO steward team member, yeah the name does sound scary :sweat_smile:, but I can assure you that this is just a naming issue, it works like this:

“the “whitelist” is to speed up this process so testers (like me) can start testing without waiting. We just finished a meeting and agreed that the name is quite misleading, maybe “voter snapshot” would be better.”

The response from the 1.1 team was not appropriate for the concern and the team bears responsibility for this. If this were handled differently, we would have been having this conversation 3 months ago, in a very different context.

The concern was made apparent to the team and it was dismissed, misleading information was then conveyed to people that were concerned. My point is that amidst defending your time and work, please accept responsibility for your role in the situation and integrate this into what you are saying.

Centralized vs Decentralized

This comment is problematic. If the 1.1 server is down, users cannot register, nor can they get their SMT proofs to be able to vote. It is this contradiction between centralized and decentralized which is causing such an issue.

If the system were centralized, responsibility would be clear. With v1.1, it is not, and the team does not demonstrate the kind of domain ownership one would expect when proposing to operate infrastructure critical to the process. We need to make sense of this centralized vs decentralized thing.

Looking forward

As Baiyu said in Telegram

“In fact, we also hope that the community can propose a more decentralized solution that does not require major changes to the current approach, and we would even be willing to extend the development period by one or two months for this purpose.”

This is the kind of attitude that is needed here, I understand your role as technical lead makes you protective of the implementation, but I can say that your posturing does not help the process.

While it may be the case that large scale changes would be needed to accomplish certain objectives, we don’t know that without exploring the alternatives, and your approach to the conversation (which presumes changes would be extraordinary) makes it extremely difficult to work through the process.

In a recent poll in Nervos Nation, 57% of responders supported the implementation as is. People want to see progress. Do with that information what you feel is appropriate, it is certainly what I am doing.

What we can say is that there are no voices in that silent majority who disagree strongly with the issues being raised.

As in all challenging things in life, the most important thing is starting with the person in the mirror. Overall “the community” will look at the entirety of this as yet another example of the challenges the project faces in executing.

Let’s put the emotions aside and just get to work on fixing things.